作者:乐天JOE
题目链接
题目描述
Bessie likes downloading games to play on her cell phone, even though she doesfind the small touch screen rather cumbersome to use with her large hooves.
She is particularly intrigued by the current game she is playing.The game starts with a sequence of N N N positive integers ( 2 ≤ N ≤ 248 2 \leq N\leq 248 2≤N≤248), each in the range 1 … 40 1 \ldots 40 1…40. In one move, Bessie cantake two adjacent numbers with equal values and replace them a singlenumber of value one greater (e.g., she might replace two adjacent 7s with an 8). The goal is to maximize the value of the largest numberpresent in the sequence at the end of the game. Please help Bessiescore as highly as possible!
给定一个1*n的地图,在里面玩2048,每次可以合并相邻两个(数值范围1-40),问序列中出现的最大数字的值最大是多少。注意合并后的数值并非加倍而是+1,例如2与2合并后的数值为3。
输入格式
The first line of input contains N N N, and the next N N N lines give the sequence
of N N N numbers at the start of the game.
输出格式
Please output the largest integer Bessie can generate.
样例 #1
样例输入 #1
4
1
1
1
2
样例输出 #1
3
提示
In this example shown here, Bessie first merges the second and third 1s to
obtain the sequence 1 2 2, and then she merges the 2s into a 3. Note that it is
not optimal to join the first two 1s.
思路
这一题需要用到区间dp这一个算法,不会的可以先移步这一题:石子合并——这是区间dp的模板题。
看到题目:在一个 1 × n 1 \times n 1×n的地图。说明什么,这是1个 一维序列 ,也就是1条 线 。
那用的算法就很容易能看出来了,用区间dp。
至于原因,是因为区间dp是在一条线上进行的,而且更好进行合并的步骤。
由于是2048游戏,所以还有1个隐藏条件:合并的两个数必须是相同且相邻的。(这点在翻译中没有体现)
来考虑dp的五个步骤。
1.状态表示:
区间 [i,j]
dp[i][j]表示的是:序列第i个数,放到第j个数区间内,全部合并的最大值。
2.阶段划分
由于是区间dp,那么显而易见,实际上我也不确定
小区间–>大区间
3.状态转移
合并代价:+1
i为左端点,j为右端点,k为断点
if(当前数字和下一个数字相同)
dp[i][j]=max(dp[i][j],dp[i][k]+1);
这是本来的状态转移方程,有什么问题吗?
有,那就是dp[i][j]是全部合并而且可以不用相邻的合并,所以答案有可能是零或比较大的数像下面这几组数据:
样例1
3
7 8 7
样例2
3
1 2 3
第一个按照上面的状态转移方程答案会是9,因为不是相邻的两个7会合并。而第二个会是0,因为不能全部合并。
那我们就用一个值(ans)用来存合并后区间[i,j]内的最大数字。
那正确的状态转移方程就很好写了:
if(当前数字和下一个数字相同)
{
dp[i][j]=max(dp[i][j],dp[i][k]+1);
ans=max(ans,dp[i][j]);
}
4.边界(初始值)设定
很简单,只需要把每个数设定为一个小区间,那么全部合并的最大答案就是那个数。
每次输入x,答案应该是区间[i,i]为这个数,其他为0:
for(int i=1;i<=n;i++)
{
cin>>x;
dp[i][i]=x;
}
5.最终答案
首先想到的是dp[1][n],但看到状态转移的部分,dp[1][n]是全部合并,而我们耗费了那么多的精力去创造的ans不是正好适合吗?
那么最后输出的答案就是ans。
坑点
这题有一些坑点:
- 状态转移中说过的ans
- 左端点与右端点的加一减一要搞清楚,不然要调很久,
亲身经历 - 搞清楚每个循环中每个变量是干嘛的,不要直接套模板
最后就可以开开心心的写代码了!
code
最后看代码
#include<bits/stdc++.h>
#define int long long
#define itn int
using namespace std;
int n,x,dp[255][255],ans;
signed main()
{
cin>>n;
for(int i=1;i<=n;i++) //初始化
{
cin>>x;
dp[i][i]=x;
}
for(int len=2;len<=n;len++) //len是遍历长度的,不需要从1开始
for(int i=1;i<=n-(len-1);i++) //左端点
{
int j=i+len-1; //右端点
for(int k=i;k<j;k++) //断点
if(dp[i][k]==dp[k+1][j]) //如果左右相等(可以进行合并)
{
dp[i][j]=max(dp[i][j],dp[i][k]+1); //合并(也可以是dp[k+1][j]+1)
ans=max(ans,dp[i][k]+1); //更新答案
}
}
cout<<ans; //输出区间[1,n]的最大数字
return 0;
}
AC记录
本人是个蒟蒻,学艺不精。如果有错误或建议请在评论区指出。
十分感谢