【原题题面】
【题面大意】
给定一个1*N(2<=N<=248/262144) 的地图,每次可以合并相邻两个(数值范围1-40),问最大能合出多少。注意合并后的数值并非加倍而是+1,例如2与2合并后的数值为3。
【题解】
【壹/248】
区间dp。
f[l][r]表示从l到r的合并后最大值,注意合并时必须满足f[l][k]==f[k+1][r]。
初值为f[i][i] = a[i],因为要求max所以其余赋值为-inf或0也行。
答案为每个f[l][r]取max的值。
【code1】
#include<bits/stdc++.h> using namespace std; #define File "248" #define ll long long inline void file(){ freopen(File".in","r",stdin); freopen(File".out","w",stdout); } 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<<1)+(x<<3)+ch-'0'; ch=getchar();} return x*f; } const int mxn = 2e3+3; int n,mx; int a[mxn]; int f[mxn][mxn]; inline int MAX(int x,int y){ return x < y ? y : x; } /* 1.初值 2.循环范围 3.转移 4.答案 */ int main(){ // file(); n = read(); for(register int i = 1;i <= n; ++i) a[i] = read(),mx = MAX(mx,a[i]); memset(f,0,sizeof f); for(int i = 1;i <= n; ++i) f[i][i] = a[i]; for(register int len = 2;len <= n; ++len){ for(register int l = 1;l <= n-len+1; ++l){ int r = l+len-1; for(register int k = l;k < r; ++k) if(f[l][k]==f[k+1][r]) f[l][r] = MAX(f[l][r],f[l][k]+1); mx = MAX(mx,f[l][r]); } } printf("%d\n",mx); return 0; } /* 4 1 1 1 2 */View Code
【贰/261244】
仔细确认发现跟前一题确确实实是一样的,n^3的区间dp当然是死翘翘了。
观察题目的特性:
序列每个数的范围极其小,所以可以考虑用它来搞一波事情。
考虑dp状态的设置:
一维是能合并出来的数字的大小
一维是下标的值
如果将f值设为能合并出来的数的大小->无法转移,不满足最优子结构
将f值设置为下标,类似倍增。
f[i][j]表示左端点为j时,能合并出的最大值为i的区间的右端点。
这样设置状态能保证往后转移。
转移:f[i][j] = f[i-1][f[i-1][j]];
初值:f[a[i]][i] = i+1;
答案:i的值。
关于58:
因为给定的原序列最大值为40,极限情况下,相邻两数的合并每次会给最大值加一,那么最多会加到58。
当然循环到59,60...什么的也没问题,如果时间复杂度过得去的话...
【code2】
#include<bits/stdc++.h> using namespace std; #define File "262144" #define ll long long inline void file(){ freopen(File".in","r",stdin); freopen(File".out","w",stdout); } 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<<1)+(x<<3)+ch-'0'; ch=getchar();} return x*f; } const int mxn = (1<<18)+5; int n; int f[63][mxn]; /* 1.转移->f[i][j] = f[i-1][f[i-1][j]] 2.循环 3.初值 4.答案 */ int ans; int main(){ //file(); n = read(); for(int i = 1;i <= n; ++i){ int t = read(); f[t][i] = i+1; } for(int i = 2;i <= 58; ++i){ for(int j = 1;j <= n; ++j){ if(!f[i][j]) f[i][j] = f[i-1][f[i-1][j]]; if(f[i][j]) ans = max(i,ans); } } printf("%d\n",ans); return 0; }View Code