目录
一、戳西瓜
题目描述
有 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;
}
注:本文章为个人学习总结,如有错误请批评指出啊( ̄︶ ̄)↗