【题解】P3147 [USACO16OPEN]262144 P(DP)
【题解】P3147 [USACO16OPEN]262144 P
虽然是道绿题,但我还是挂/卡了很久,是道好题,写篇题解记录一下。
题目链接
P3147 [USACO16OPEN]262144 P - 洛谷
下面这道题是上面的弱化版,只是数据范围不同,但有一种解法只能通过 P3147,所以这里都以 P3147 为例解释(P3147 的 AC 代码可以通过 P3146),但是会讲到两种解法。
题意概述
有一个长为
思路分析
真的是道妙题,单从线性 DP 的角度思考我愣是想不出来 DP 状态;而从区间 DP 的角度想又做法假了。这里记录线性 DP 和区间 DP 两种解法。
解法一:线性 DP
要合成一个数
定义
发现这玩意跟倍增有点相似。
那么有:
画个图解释一下:
相当于是从
那么就相当于找到了两段相邻的最大值为
这么妙的做法我怎么就想不到呢。/kk
那么我们只需要在刚开始输入时,初始化
然后枚举
最后枚举
时间复杂度:
解法二:区间 DP
注:这种解法是针对 P3146 的,由于 P3147 的数据范围较大,此种做法行不通。
相对于这个题的线性 DP,区间 DP 是更好想的。
看到“合并”,应该一眼想到区间 DP。
我当时心想:这题如果用区间 DP 未免也太简单了,然后立马开写,结果却 WA 了,只有 我真菜。。
这里记录一下我的思路历程。
刚开始想,这题不就是石子合并(弱化版) - 洛谷的变形嘛。
用
-
当
时, 。即: 和 是两段能合成最大值相同的区间,答案即为两段的 值 ; -
当
时, 。即:若两段所能合成的最大值不相同,则区间 在所有这些区间中取最大值。
最后答案是 看了眼评论区发现我的做法假了。
Hack:
3
2 1 2
按照我上述思路:
在求
因为
那么怎么做呢。
可以用一个变量
对于每个
注意
时间复杂度:
代码实现
解法一
//luoguP3147
//线性 DP 解法
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=262150;
int a[maxn],dp[65][maxn];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main()
{
int n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
dp[a[i]][i]=i+1;
}
int ans=0;
for(int i=2;i<=58;i++)
{
for(int j=1;j<=n;j++)
{
if(!dp[i][j])dp[i][j]=dp[i-1][dp[i-1][j]];
if(dp[i][j])ans=i;
}
}
cout<<ans<<endl;
return 0;
}
/*dp[i][j] 表示左端点为 j,能合并出 i 的右端点位置。
那么 dp[i][j]=dp[i-1][dp[i-1][j]].
可以发现数字大小最大为 40+log n = 58.
这题怎么这么妙啊!我为什么就想不到!
*/
解法二
//luoguP3146
//区间 DP 解法
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=255;
int dp[maxn][maxn],a[maxn];
int ans;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main()
{
int n;
n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();dp[i][i]=a[i];
ans=max(ans,a[i]);
}
for(int len=2;len<=n;len++)
{
for(int l=1;l+len-1<=n;l++)
{
int r=l+len-1;
for(int k=l;k<r;k++)
{
if(dp[l][k]==dp[k+1][r])dp[l][r]=max(dp[l][r],dp[l][k]+1);
ans=max(dp[l][r],ans);
}
// cout<<l<<" "<<r<<" "<<dp[l][r]<<endl;
}
}
cout<<ans<<endl;
return 0;
}
/*3
2 1 2
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】