一些区间DP题目的の总结

本文介绍了如何使用动态规划策略解决戳西瓜和玩雪的编程问题。通过分析题目,确定状态转移方程,给出代码实现。戳西瓜问题中,区间DP模板用于找到最大收益;玩雪的小Y问题同样采用区间DP,断环为链处理;合唱队问题涉及队形排列,通过四种情况的if判断完成状态转移。
摘要由CSDN通过智能技术生成

目录

一、戳西瓜

 (1)题目分析

(2)代码实现

二、玩雪的小Y

 (1)题目分析

(2)代码实现

 三、合唱队(P3205 [HNOI2010]合唱队)

 (1)题目分析

(2)代码实现


一、戳西瓜

题目描述

有 n 个西瓜,编号为0 到 n-1,每个西瓜上都标有一个数字,这些数字存在数组 nums 中。

现在要求你戳破所有的西瓜。每当你戳破一个西瓜 i 时,你可以获得 nums[left] *nums[i] *nums[right] 个硬币。 这里的 left 和 right 代表和 i 相邻的两个西瓜的序号。注意当你戳破了西瓜 i 后,西瓜 left 和西瓜 right 就变成了相邻的西瓜。

求所能获得硬币的最大数量。

输入格式

共两行。

第一行:一个正整数n,代表西瓜个数。

第二行:共n个数字,每个数字表示西瓜上标的数字。

输出格式

一行,一个整数,获得硬币的最大数量。

样例

样例输入

复制4
3 1 5 8

样例输出

复制167 

解释: 
nums = [3,1,5,8] --> [3,5,8] -->   [3,8]   -->  [8]  --> []
coins =  3*1*5      +  3*5*8    +  1*3*8      + 1*8*1   = 167

数据范围与提示

你可以假设 nums[-1] = nums[n] = 1,但注意它们不是真实存在的西瓜,所以并不能被戳破。

0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

 (1)题目分析

        区间DP题目一般有一个模板:

dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + w[l][r])

         其中l表示区间的左端点,r表示区间的右端点,k表示在l~r这段区间内的一个点。核心思想可以看作把一个大区间不断地划分为小区间,由小区间模拟合并过程来求得大区间的值。戳西瓜就比较典型。我们在处理的时候,可以把区间看作一个“西瓜”,即dp[i][k]表示在i~k这段区间内一顿操作后幸存的那个西瓜,它身上背负了这段区间内其他西瓜的值,现在他要和右边的另一个区间合并,就遵从题目的规则,写出了转移方程。

(2)代码实现

#include<cstdio>
#include<algorithm>
using namespace std;
int dp[505][505];
int a[505];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	a[0]=1;
	a[n+1]=1;//假设 nums[-1] = nums[n] = 1 但它们不是真实存在的西瓜,并不能被戳破
	for(int l=1;l<=n+1;l++){//区间长度l
		for(int i=0;i+l<=n+1;i++){//左端点i
			int j=i+l;//右端点j
			for(int k=i+1;k<j;k++){中间节点k
				dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]+a[k]*a[i]*a[j]);
				//printf("%d ",dp[i][j]);
			}
		}
	} 
	printf("%d",dp[0][n+1]);
	return 0;
}

二、玩雪的小Y

题目描述

小Y喜欢将他喜爱的雪花摆成一个环,然后他会两两将雪花用魔力合并成为一个大雪花。

雪花自身具有两个数值,光魔法值和暗魔法值,一个雪花的光魔法值是它上一个雪花的暗魔法值,而雪花的光魔法值会告诉你。比如3 4 5这三个雪花,它们的数值就是

(3,4),(4,5),(5,3)。将两个雪花合并时会释放 先前一个雪花光魔法值 X 先前一个雪花暗魔法值 X 后面一个雪花的暗魔法值 的魔力,同时合并后的雪花因为魔力的作用

光魔法值将变为原来前一个雪花的光魔法值,暗魔法值将变为后一个雪花的暗魔法值。比如将(3,4),(4,5)这两个雪花合并,释放的魔力为345,合成的雪花的光魔法值为

3,暗魔法值为5。

大魔法师小Y的数学并不好,所以他希望你能帮他计算怎么合并释放的魔法值最高。

输入格式

第一行一个正整数 

第二行  个不超过  的正整数,第  个数为第  个雪花的光魔法值。

至于雪花的顺序,你可以这样理解:将雪花放在桌面上,不要出现交叉,随机指定一颗珠子为第一颗珠子,按顺时针确定其它珠子的顺序。

输出格式

输出只有一行,一个不超过  的正整数,表示最优合并所释放的魔法值。

样例

样例输入

复制4
2 3 5 10

样例输出

复制710

样例解释

 X代表合并 

数据范围与提示

对于100%的数据,4<=n<=100。

 (1)题目分析

        一道经典的区间dp题目,其中有一个重要的处理方法——断环为链。将数组开成两倍再进行处理,其它同上题。

(2)代码实现

#include<cstdio>
#include<algorithm>
using namespace std;
int n,light[1005],dark[1005],dp[1005][1005],ans;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&light[i]);
		dark[i-1]=light[i];
	}
	dark[n]=light[1];
	for(int i=n+1;i<=2*n;i++)//断环为链
		light[i]=light[i-n],dark[i]=dark[i-n];
	for(int l=1;l<n;l++)
	{
		for(int i=1;i+l<=2*n;i++)
		{
			int j=i+l;
			for(int k=i;k<j;k++)
			{
				dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+light[i]*dark[k]*dark[j]);	
				ans=max(dp[i][j],ans);
			}
		}
	}
	printf("%d",ans);
}

 三、合唱队(P3205 [HNOI2010]合唱队

题目描述

为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A 需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共 nn 个人,第 i个人的身高为 h_i​ 米(1000 \le h_i \le 20001000≤hi​≤2000),并已知任何两个人的身高都不同。假定最终排出的队形是 AA 个人站成一排,为了简化问题,小 A 想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:

  • 第一个人直接插入空的当前队形中。

  • 对从第二个人开始的每个人,如果他比前面那个人高(h 较大),那么将他插入当前队形的最右边。如果他比前面那个人矮(h较小),那么将他插入当前队形的最左边。

当 nn 个人全部插入当前队形后便获得最终排出的队形。

例如,有 66 个人站成一个初始队形,身高依次为 1850, 1900, 1700, 1650, 1800, 17501850,1900,1700,1650,1800,1750,
那么小 A 会按以下步骤获得最终排出的队形:

  • 18501850。

  • 1850, 19001850,1900,因为 1900 > 18501900>1850。

  • 1700, 1850, 19001700,1850,1900,因为 1700 < 19001700<1900。

  • 1650, 1700, 1850, 19001650,1700,1850,1900,因为 1650 < 17001650<1700。

  • 1650, 1700, 1850, 1900, 18001650,1700,1850,1900,1800,因为 1800 > 16501800>1650。

  • 1750, 1650, 1700, 1850, 1900, 18001750,1650,1700,1850,1900,1800,因为 1750 < 18001750<1800。

因此,最终排出的队形是 1750, 1650, 1700, 1850, 1900, 18001750,1650,1700,1850,1900,1800。

小 A 心中有一个理想队形,他想知道多少种初始队形可以获得理想的队形。

请求出答案对 1965082719650827 取模的值。

输入格式

第一行一个整数 nn。
第二行 nn 个整数,表示小 A 心中的理想队形。

输出格式

输出一行一个整数,表示答案 \bmod 19650827mod19650827 的值。

输入输出样例

输入 #1复制

4
1701 1702 1703 1704

输出 #1复制

8

说明提示

对于 30\%30% 的数据,n \le 100n≤100。
对于 100\%100% 的数据,n \le 1000n≤1000,1000 \le h_i \le 20001000≤hi​≤2000。

 (1)题目分析

        对于一个队列 i~j 而言,他可能 由 i+1~j 或是 i~j-1 而来,而之所以会变为i~j,有以下几种情况:①新加入的之前加入的那个人,站在了队首;新加入的人因为比他矮,站在了他前面成为了新队首i。②新加入的之前加入的那个人,站在了队首;新加入的人因为比他高,站在了队伍最后成为了新队尾j。③新加入的之前加入的那个人,站在了队尾;新加入的人因为比他矮,站在了他前面成为了新队首i。④新加入的之前加入的那个人,站在了队尾;新加入的人因为比他高,站在了队伍最后成为了新队尾j。这样四个if完成转移。

(2)代码实现

#include<cstdio>
int const mod=19650827;
int n,a[1005],f[1005][1005][2];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) f[i][i][0]=1;
	for(int len=1;len<n;len++){
		for(int i=1;i+len<=n;i++){
			int j=i+len;
			if(a[i]<a[i+1]) f[i][j][0]=(f[i][j][0]+f[i+1][j][0])%mod;
			if(a[i]<a[j]) f[i][j][0]=(f[i][j][0]+f[i+1][j][1])%mod;
			if(a[j]>a[j-1]) f[i][j][1]=(f[i][j][1]+f[i][j-1][1])%mod;
			if(a[j]>a[i]) f[i][j][1]=(f[i][j][1]+f[i][j-1][0])%mod;
		}
	}
	printf("%d",(f[1][n][1]+f[1][n][0])%mod);//不确定最后一个人是怎么进队的,要将两种情况相加
	return 0;
}

注:本文章为个人学习总结,如有错误请批评指出啊( ̄︶ ̄)↗ 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值