浅说区间dp(下)

文章目录

  • 环形区间dp
  • 例题
  • [NOI1995] 石子合并
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 思路
  • [NOIP2006 提高组] 能量项链
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 思路
  • [NOIP2001 提高组] 数的划分
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 思路
  • 放苹果
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 样例 #2
      • 样例输入 #2
      • 样例输出 #2
    • 提示
    • 思路
  • 总结

环形区间dp

上一讲我们主要是讲解的链式的区间dp,但是我们经常会遇见一个环的dp问题,那么我们这时候应该怎么办呢,我们还是以一个生活例子来引入
又是wjq~~
wjq又要开始合并衣服了,最近的wjq中了邪,喜欢圆圆的东西,所以这一次她把衣服放成了一个圆圈,总计有 N 件衣服,每件衣服有一个遮挡程度,现要将衣服有次序地合并成一堆,规定每次只能选相邻的 2 件合并成新的一堆,并将新的一堆的遮挡程度,记为该次合并的得分。
首先我们不难想到,每一件衣服都有可能是起点,所以我们现在就有以下几种情况:

假设现在圆圈里有“黑丝”,“白丝”,“泳装”,“jk”
那么我们就有:
“黑丝”,“白丝”,“泳装”,“jk”
“白丝”,“泳装”,“jk”,“黑丝”
“泳装”,“jk”,“黑丝”,“白丝”
“泳装”,“jk”,“黑丝”,“白丝”
总计四种情况

如果我们去找起点的话,太过繁琐了,那么我们来想想怎么优化。不难发现,当我们求解“黑丝”,“白丝”,“泳装”,“jk”时,“白丝”,“泳装”,“jk”,已经有了答案,那么我们在计算“白丝”,“泳装”,“jk”,“黑丝”的时候,就不用再次计算“白丝”,“泳装”,“jk”了,所以我们这里可以将整个数组copy一遍,放到后面,这样我们就可以避免重复计算了
这样无论哪个点为起点,再向后面枚举3个点都是一个完整的区间,即区间[i,i+n-1]为一个完整的区间。环变链后再做一次和前面一样的dp,注意下范围和边界,最后因为所有的长度为n的区间都有可能是答案,所以答案在min(dp[i,i+n-1])中。
要注意i要枚举到n的后面,因为后面的dp会用到这些值
这一招叫做化环为链,长度翻倍,是区间dp问题中常见的处理手段


例题

[NOI1995] 石子合并

题目描述

在一个圆形操场的四周摆放 N N N 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 2 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出一个算法,计算出将 N N N 堆石子合并成 1 1 1 堆的最小得分和最大得分。

输入格式

数据的第 1 1 1 行是正整数 N N N,表示有 N N N 堆石子。

2 2 2 行有 N N N 个整数,第 i i i 个整数 a i a_i ai 表示第 i i i 堆石子的个数。

输出格式

输出共 2 2 2 行,第 1 1 1 行为最小得分,第 2 2 2 行为最大得分。

样例 #1

样例输入 #1

4
4 5 9 4

样例输出 #1

43
54

提示

1 ≤ N ≤ 100 1\leq N\leq 100 1N100 0 ≤ a i ≤ 20 0\leq a_i\leq 20 0ai20

思路

这道题其实和上面的引入大同小异,可以直接套用

#include <iostream>
using namespace std;
const int N = 300;
const int INF = 10e9;
int n, dp[N][N], dp2[N][N];
int sum[N], s[N];

int main(){
	cin >> n;
	for (int i = 1; i <= n; i++) {
 	   cin >> s[i];
 	   s[i + n] = s[i];
	}
	for(int i = 1; i <= n * 2; i++){
		sum[i] = s[i] + sum[i - 1];
  	}
    for(int len = 1; len < n; len++){
   		for (int i = 1; i <= (n * 2 - len); i++){
      		int j = i + len;
        	dp[i][j] = INF;
        	for(int k = i; k < j; k++){
        		dp[i][j] = min (dp[i][j], dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]);
       			dp2[i][j] = max (dp2[i][j], dp2[i][k] + dp2[k + 1][j] + sum[j] - sum[i - 1]);
       		}
        }
    }
    int ans = INF, ans2 = 0;
    for(int i = 1; i <= n; i++){
    	ans = min(ans, dp[i][i + n - 1]);
    	ans2 = max(ans2,dp2[i][i + n - 1]);
	}
    cout << ans << endl << ans2;
}

[NOIP2006 提高组] 能量项链

题目描述

在 Mars 星球上,每个 Mars 人都随身佩带着一串能量项链。在项链上有 N N N 颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是 Mars 人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为 m m m,尾标记为 r r r,后一颗能量珠的头标记为 r r r,尾标记为 n n n,则聚合后释放的能量为 m × r × n m \times r \times n m×r×n(Mars 单位),新产生的珠子的头标记为 m m m,尾标记为 n n n

需要时,Mars 人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不同的,请你设计一个聚合顺序,使一串项链释放出的总能量最大。

例如:设 N = 4 N=4 N=4 4 4 4 颗珠子的头标记与尾标记依次为 ( 2 , 3 ) ( 3 , 5 ) ( 5 , 10 ) ( 10 , 2 ) (2,3)(3,5)(5,10)(10,2) (2,3)(3,5)(5,10)(10,2)。我们用记号 ⊕ \oplus 表示两颗珠子的聚合操作, ( j ⊕ k ) (j \oplus k) (jk) 表示第 j , k j,k j,k 两颗珠子聚合后所释放的能量。则第 4 4 4 1 1 1 两颗珠子聚合后释放的能量为:

( 4 ⊕ 1 ) = 10 × 2 × 3 = 60 (4 \oplus 1)=10 \times 2 \times 3=60 (41)=10×2×3=60

这一串项链可以得到最优值的一个聚合顺序所释放的总能量为:

( ( ( 4 ⊕ 1 ) ⊕ 2 ) ⊕ 3 ) = 10 × 2 × 3 + 10 × 3 × 5 + 10 × 5 × 10 = 710 (((4 \oplus 1) \oplus 2) \oplus 3)=10 \times 2 \times 3+10 \times 3 \times 5+10 \times 5 \times 10=710 (((41)2)3)=10×2×3+10×3×5+10×5×10=710

输入格式

第一行是一个正整数 N N N 4 ≤ N ≤ 100 4 \le N \le 100 4N100),表示项链上珠子的个数。第二行是 N N N 个用空格隔开的正整数,所有的数均不超过 1000 1000 1000。第 i i i 个数为第 i i i 颗珠子的头标记( 1 ≤ i ≤ N 1 \le i \le N 1iN),当 i < N i<N i<N 时,第 i i i 颗珠子的尾标记应该等于第 i + 1 i+1 i+1 颗珠子的头标记。第 N N N 颗珠子的尾标记应该等于第 1 1 1 颗珠子的头标记。

至于珠子的顺序,你可以这样确定:将项链放到桌面上,不要出现交叉,随意指定第一颗珠子,然后按顺时针方向确定其他珠子的顺序。

输出格式

一个正整数 E E E E ≤ 2.1 × 1 0 9 E\le 2.1 \times 10^9 E2.1×109),为一个最优聚合顺序所释放的总能量。

样例 #1

样例输入 #1

4
2 3 5 10

样例输出 #1

710

提示

NOIP 2006 提高组 第一题

思路

该题和合并石子(环)在处理方式上一样,都是通过把小区间合并成大区间,枚举合并两个区间的中间节点,计算出整个区间合并的最大能量因为也是一个环,所以我们还是先把环拆链,长度加倍,注意枚举顺序,因为是把小区间合并成大区间,所以先枚举区间长度,再枚举区间左端点,计算出区间右端点,最后再枚举最后一次合并的点,也就是断点,再计算答案。还是和前面一样,注意边界问题,区间长度不能大于n,也不能小于1.左右端点的范围不能小于或者大于真实情况。

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 301;

int dp[MAX_N][MAX_N];
int a[MAX_N];
int maxn=INT_MIN;

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        a[n+i]=a[i];
    }
    for (int len = 1; len <= 2*n; ++len) {
        for (int i = 1; i + len - 1 <= 2*n; ++i) {
            int j = i + len - 1;
            for (int k = i+1; k < j; ++k) {
                dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + a[i]*a[k]*a[j]);
            }
        }
    }
	for (int i=1;i<=n;i++){
		maxn=max(dp[i][i+n],maxn);
	}
    cout << maxn ;

    return 0;
}

[NOIP2001 提高组] 数的划分

题目描述

将整数 n n n 分成 k k k 份,且每份不能为空,任意两个方案不相同(不考虑顺序)。

例如: n = 7 n=7 n=7 k = 3 k=3 k=3,下面三种分法被认为是相同的。

1 , 1 , 5 1,1,5 1,1,5;
1 , 5 , 1 1,5,1 1,5,1;
5 , 1 , 1 5,1,1 5,1,1.

问有多少种不同的分法。

输入格式

n , k n,k n,k 6 < n ≤ 200 6<n \le 200 6<n200 2 ≤ k ≤ 6 2 \le k \le 6 2k6

输出格式

1 1 1 个整数,即不同的分法。

样例 #1

样例输入 #1

7 3

样例输出 #1

4

提示

四种分法为:
1 , 1 , 5 1,1,5 1,1,5;
1 , 2 , 4 1,2,4 1,2,4;
1 , 3 , 3 1,3,3 1,3,3;
2 , 2 , 3 2,2,3 2,2,3.

【题目来源】

NOIP 2001 提高组第二题

思路

首先因为这道题的数据范围比较小,所以我们可以直接打dfs,但是我们想象,如果N的范围比较大呢,自然,我们还是用动态规划
该题是一道相对比较复杂的区间DP,看上去和前面的区间DP类似,以每次划分作为阶段,枚举最后一次划分的位置k,那么求出所有dp[k][j-1]之和即为答案。这种解决办法是错误的,因为存在重复方案,为了避免重复,最后一次分的数量不能比前面少,但是这种做法没有考虑这个问题。
该题正确的做法是考虑分解方案中是否有一份中只有1个方案和没有一个的方案。如果有一个,那么答案相当于是dp[i-1][j-1],如果没有,那么每一份都减少一个1的方案也是合理的,即dp[i-j][j]。

#include<bits/stdc++.h>
using namespace std;

int dp[2100][2100];
int main(){
	int n,k;
	cin>>n>>k;
	for (int i=1;i<=n;i++){
		dp[i][0]=1;
		dp[i][1]=1;
	}
	for (int x=2;x<=k;x++) {
	    dp[1][x]=0;
	    dp[0][x]=0;
	}
	for (int i=2;i<=n;i++){//i个数 
		for (int x=2;x<=k;x++){//每一个位置 
			if (i<=x)dp[i][x]=dp[i-1][x-1];
			else dp[i][x]=(dp[i-1][x-1]+dp[i-x][x]);
		}
	}
	cout<<dp[n][k];
	return 0;
}

放苹果

题目描述

m m m 个同样的苹果放在 n n n 个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法。( 5 , 1 , 1 5,1,1 5,1,1 1 , 1 , 5 1,1,5 1,1,5 是同一种方法)

输入格式

第一行是测试数据的数目 t t t,以下每行均包括二个整数 m m m n n n,以空格分开。

输出格式

对输入的每组数据 m m m n n n,用一行输出相应的结果。

样例 #1

样例输入 #1

1
7 3

样例输出 #1

8

样例 #2

样例输入 #2

3
3 2
4 3
2 7

样例输出 #2

2
4
2

提示

对于所有数据,保证: 1 ≤ m , n ≤ 10 1\leq m,n\leq 10 1m,n10 0 ≤ t ≤ 20 0 \leq t \leq 20 0t20

思路

同样的,这道题我们还是考虑动态规划。
该题和上一道题的解题思路一样,如果上一道题理解了,那么该题很容易想出状态转移方程。对于每一个盘子,最少放0个,如果存在放0个的盘子,答案为dp[i][j-1],如果不存在放0个的盘子,从每个盘子里面都拿掉一个,答案为 d p [ i − j ] [ j ] dp[i-j][j] dp[ij][j]
该题的难点不仅仅在于状态转移方程,还有初始状态。首先,i<j,上一道题为0,所以不用处理,但是该题当i<j时, d p [ i ] [ j ] = d p [ i ] [ i ] dp[i][j]=dp[i][i] dp[i][j]=dp[i][i],如果不单独处理那么状态转移中的 d p [ i − j ] [ j ] dp[i-j][j] dp[ij][j]就不存在,因为i-j为负数。
状态转移方程:
d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + d p [ i − j ] [ j ] ; dp[i][j]=dp[i][j-1]+dp[i-j][j]; dp[i][j]=dp[i][j1]+dp[ij][j];当i,j都从1开始枚举时,需要用到 d p [ i ] [ 0 ] dp[i][0] dp[i][0],这到底算一算一种方案,我们也不太好确定,所以j最好从2开始枚举,这样我们需要初始化 d p [ i ] [ 1 ] dp[i][1] dp[i][1]的值,很明显 d p [ i ] [ 1 ] = 1 dp[i][1]=1 dp[i][1]=1
还需要用到 d p [ i − j ] [ j ] dp[i-j][j] dp[ij][j]的值,我们先判断i,j的大小,此时i>=j,我们会用到 d p [ 0 ] [ j ] dp[0][j] dp[0][j]的值,我们还需要初始化 d p [ 0 ] [ j ] dp[0][j] dp[0][j]的值,很明显也为1。

#include<bits/stdc++.h>
using namespace std;

int dp[2100][2100];
int main(){
	int n,k,t;
	cin>>t;
	while (t--){
		memset(dp,0,sizeof(dp));
		cin>>n>>k;
		for (int i=1;i<=n;i++){
			dp[i][0]=1;
			dp[i][1]=1;
		}
		for (int i=1;i<=k;i++){
			dp[0][i]=1;
			dp[1][i]=1;
		}
		for (int i=2;i<=n;i++){//i个数 
			for (int x=2;x<=k;x++){//每一个位置 
				if (i<x)dp[i][x]=dp[i][i];
				else dp[i][x]=(dp[i][x-1]+dp[i-x][x])%1000007;
			}
		}
		cout<<dp[n][k]<<endl;;
	}

	return 0;
}

总结

好了,到目前为止,普及组所要用到的动态规划问题我们基本上是讲完了,不知道大家有没有收获呢?如果有问题,欢迎到评论区留言,或者私信博主,如果喜欢博主的博客的话,请点一个赞,蟹蟹~~

请添加图片描述

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/802666.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI大模型加持的新一代网络舆情系统——“速途观澜”舆情感知引擎发布上线

**近日&#xff0c;AI和数据驱动的新媒体生态服务商速途网络&#xff0c;发布上线企业声誉管理智能服务平台“速途观澜”。**该平台融合了速途最新研发升级的“观澜舆情感知引擎”&#xff0c;一款以大数据和AI为底座的网络舆情态势感知系统&#xff0c;这是速途在产品创新研发…

orcad导出pdf 缺少title block

在OrCAD中导出PDF时没有Title Block 最后确认问题在这里&#xff1a; 要勾选上Title Block Visible下面的print

共建特色基地 协同互促育人

作为芯片和集成电路、人工智能、智能网联车等临港重点产业布局的知识密集型相关企业&#xff0c;核心技术人才和技术骨干是公司参与全球竞争的重要核心竞争力之一。 知从科技通过不断的创新和规范&#xff0c;在深化产教融合、校企合作、“双师型”、联合办学协同育人、产业人…

Kafka Producer发送消息流程之分区器和数据收集器

文章目录 1. Partitioner分区器2. 自定义分区器3. RecordAccumulator数据收集器 1. Partitioner分区器 clients/src/main/java/org/apache/kafka/clients/producer/KafkaProducer.java&#xff0c;中doSend方法&#xff0c;记录了生产者将消息发送的流程&#xff0c;其中有一步…

【XSS】

文章目录 0x01 简介0x02 XSS Payload用法XSS攻击平台及调试JavaScript 0x03 XSS构造技巧XSS漏洞防御策略 跨站脚本攻击&#xff0c;Cross Site Script。&#xff08;重点在于脚本script&#xff09; 分类 反射型、存储型DOM型 漏洞原理&#xff1a;通过插入script篡改“HTML”…

字节码编程bytebuddy之通过Advice动态修改方法参数值

写在前面 本文看下如何通过bytebuddy的advice切面技术来动态修改方法入参值。 1&#xff1a;程序 首先定义premain&#xff1a; package com.dahuyou.change.method.param;//import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.AgentBu…

Java web从入门到精通 (第 2版)中文电子版

前言 《Java Web从入门到精通&#xff08;第2版&#xff09;》共分21章&#xff0c;包括Java Web应用开发概述、HTML与CSS网页开发基础、JavaScript脚本语言、搭建开发环境、JavaBean技术、Servlet技术、过滤器和监听器、Hibernate高级应用、Java Web的数据库操作、EL&#xf…

Linux 上 TTY 的起源

注&#xff1a;机翻&#xff0c;未校对。 What is a TTY on Linux? (and How to Use the tty Command) What does the tty command do? It prints the name of the terminal you’re using. TTY stands for “teletypewriter.” What’s the story behind the name of the co…

每日一题,力扣leetcode Hot100之49. 字母异位词分组

该题用哈希表解答&#xff0c;具有统一特征的作为哈希表的键名&#xff0c;然后满足要求的作为值 解法一&#xff1a; 我们将每个字符串进行排序&#xff0c;如果排序后的结果相同&#xff0c;则可以认为是字母异位词&#xff0c;我们将排序后的结果作为哈希表的key&#xff…

智能听诊器:宠物健康监测的革新者

宠物健康护理领域迎来了一项激动人心的技术革新——智能听诊器。这款创新设备以其卓越的精确度和用户友好的操作&#xff0c;为宠物主人提供了一种全新的健康监测方法。 使用智能听诊器时&#xff0c;只需将其放置在宠物身上&#xff0c;它便能立即捕捉到宠物胸腔的微小振动。…

S274多功能可编程RTU在智慧水务远程水质检测系统中的应用案例

钡铼第四代RTU S274作为一款多功能可编程的无线工业物联网数据监测采集控制短信报警终端&#xff0c;为智慧水务领域提供了强大的技术支持和解决方案。 技术概述与特点 钡铼S274基于UCOSII嵌入式实时操作系统&#xff0c;支持多种通信协议包括短信和MQTT&#xff0c;能够接入…

2024嘶吼网络安全产业图谱(高清完整版)

在数字化和智能化浪潮的推动下&#xff0c;网络安全产业正处于一个快速变革的时期。从传统的防御手段和被动的威胁应对&#xff0c;到如今主动预防和智能检测技术的普及&#xff0c;网络安全领域的焦点和需求正不断演进。为了更好的理解当前网络安全产业现状和未来发展方向&…

jeecgboot项目不知道什么原因启动出来8080端口后就不下去了,要等上10多分钟才出来接口地址等正常情况

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、项目中途不知道什么原因&#xff0c;就出现下面情况 具体如下&#xff1a; 2024-07-15 15:08:15.767 [main] [34mINFO [0;39m [36mliquibase.changelog:30[0;39m - Reading from jeec…

【LeetCode】十七、并查集

文章目录 1、并查集Union Find2、并查集find的优化&#xff1a;路径压缩 Quick find3、并查集union的优化&#xff1a;权重标记 1、并查集Union Find 并查集&#xff0c;一种树形的数据结构&#xff0c;处理不相交的两个集合的合并与查询问题。 【参考&#xff1a;&#x1f4…

优化 Java 数据结构选择与使用,提升程序性能与可维护性

优化 Java 数据结构选择与使用&#xff0c;提升程序性能与可维护性 引言 在软件开发中&#xff0c;数据结构的选择是影响程序性能、内存使用以及代码可维护性的关键因素之一。Java 作为一门广泛使用的编程语言&#xff0c;提供了丰富的内置数据结构&#xff0c;如数组、链表、…

python用selenium网页模拟时xpath无法定位元素解决方法2

有时我们在使用python selenium xpath时&#xff0c;无法定位元素&#xff0c;红字显示no such element。上一篇文章写了1种情况&#xff0c;是包含iframe的&#xff0c;详见https://blog.csdn.net/Sixth5/article/details/140342929。 本篇写第2种情况&#xff0c;就是xpath定…

嵌入式人工智能(9-基于树莓派4B的PWM-LED呼吸灯)

1、PWM简介 (1)、什么是PWM 脉冲宽度调制(PWM)&#xff0c;是英文“Pulse Width Modulation”的缩写&#xff0c;简称脉宽调制&#xff0c;是在具有惯性的系统中利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术&#xff0c;广泛应用在从测量、通信到功率控制…

Open3D 最小二乘法拟合点云平面

目录 一、概述 1.1最小二乘法原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2matplotlib可视化 3.3平面拟合方程 前期试读&#xff0c;后续会将博客加入该专栏&#xff0c;欢迎订阅 Open3D点云算法与点云深度学习…

【内网穿透】打洞笔记

文章目录 前言原理阐述公网sshfrp转发服务 实现前提第一步&#xff1a;第二步第三步第四步 补充第五步&#xff08;希望隧道一直开着&#xff09;sftp传数据&#xff08;嫌云服务器上的网太慢&#xff09; 前言 租了一个云服务器&#xff0c;想用vscode的ssh远程连接&#xff…