浅说区间dp(下)
创始人
2025-01-07 13:09:56
0

文章目录

  • 环形区间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 1≤N≤100, 0 ≤ a i ≤ 20 0\leq a_i\leq 20 0≤ai​≤20。

思路

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

#include  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) (j⊕k) 表示第 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 (4⊕1)=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 (((4⊕1)⊕2)⊕3)=10×2×3+10×3×5+10×5×10=710。

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

4 2 3 5 10 

样例输出 #1

710 

提示

NOIP 2006 提高组 第一题

思路

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

#include  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

输出格式

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 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<

放苹果

题目描述

把 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 1≤m,n≤10, 0 ≤ t ≤ 20 0 \leq t \leq 20 0≤t≤20。

思路

同样的,这道题我们还是考虑动态规划。
该题和上一道题的解题思路一样,如果上一道题理解了,那么该题很容易想出状态转移方程。对于每一个盘子,最少放0个,如果存在放0个的盘子,答案为dp[i][j-1],如果不存在放0个的盘子,从每个盘子里面都拿掉一个,答案为 d p [ i − j ] [ j ] dp[i-j][j] dp[i−j][j]。
该题的难点不仅仅在于状态转移方程,还有初始状态。首先,i 状态转移方程:
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][j−1]+dp[i−j][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[i−j][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 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

总结

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

请添加图片描述

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

相关内容

热门资讯

教程辅助“小闲巴渝辅助”有挂透... 教程辅助“小闲巴渝辅助”有挂透视开挂辅助下载2026新版技巧《详细加薇136704302咨询》游戏特...
总算了解“雀友会广东潮汕麻雀”... 雀友会广东潮汕麻雀是一款专注玩家量身打造的游戏记牌类型软件,在雀友会广东潮汕麻雀这款游戏中我们可以记...
教程辅助“青龙大厅透视挂在什么... 教程辅助“青龙大厅透视挂在什么位置”有挂教程开挂辅助插件解密教程;打开点击测试直接进入微信(1367...
推荐一款“衢州都莱辅助软件”德... 推荐一款“衢州都莱辅助软件”德普之星透视辅助软件是真的吗(带开挂辅助插件教你攻略)>>您好:软件加薇...
教程辅助“禅游指尖四川破解版”... 教程辅助“禅游指尖四川破解版”有挂规律开挂辅助器解密教程;打开点击测试直接进入微信(13670430...
透视代打“传送屋潮州激k辅助怎... 透视代打“传送屋潮州激k辅助怎么打开”hhpoker是真的还是假的(带开挂辅助软件AI教程);打开点...
教程辅助“兴动互娱脚本免费最新... 教程辅助“兴动互娱脚本免费最新版本”有挂方式开挂辅助软件爆料教程您好:兴动互娱脚本免费最新版本这款游...
查到实测“中至余干小程序破解”... 查到实测“中至余干小程序破解”wepoker手机助手(带开挂辅助神器插件教程)【无需打开直接搜索加薇...
教程辅助“微乐智能辅助app”... 教程辅助“微乐智能辅助app”有挂攻略开挂辅助安装AI教程;无需打开直接搜索薇:136704302 ...
一分钟了解“微乐小程序脚本辅助... 一分钟了解“微乐小程序脚本辅助”红龙poker辅助工具(带开挂辅助软件微扑克教程);无需打开直接搜索...