对于基本区间dp,设dp[l][r]是区间l到r的最大价值。
我们可以枚举区间的长度,在枚举左端点,判断即可。
当右端点大于n,就break。
dp[l][r]=max(dp[l+1][r]+v[l]*(n-i+1),dp[l][r-1]+v[r]*(n-i+1))
别忘了初始化,dp[i][i]=n*v[i].
代码:
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 int dp[3000][3000]; 5 int n,v[3000]; 6 int main(){ 7 scanf("%d",&n); 8 for(int i=1;i<=n;++i) 9 scanf("%d",&v[i]); 10 for(int i=1;i<=n;++i)dp[i][i]=n*v[i]; 11 for(int i=2;i<=n;++i){ 12 for(int l=1;l<=n;l++){ 13 int r=l+i-1; 14 if(r>n)break; 15 dp[l][r]=max(dp[l+1][r]+v[l]*(n-i+1),dp[l][r-1]+v[r]*(n-i+1)); 16 } 17 }printf("%d ",dp[1][n]); 18 return 0; 19 }
也可以用区间dp做。
设dp[i][j]是区间[i,j]的最佳答案。
我们枚举区间。
对于每次区间,枚举合并的交点。
当且仅当合并两方值相等时才能合并。
即
if(dp[i][k]==dp[k+1][j])dp[i][j]=max(dp[i][j],dp[i][k]+1)
代码:
#include<cstdio> #include<iostream> using namespace std; int n,dp[500][500],ans=-(1<<30); int main(){ scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%d",&dp[i][i]); for(int i=n-1;i>=1;--i){ for(int j=i+1;j<=n;++j){ 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[i][k]&dp[k+1][j]'s merge ans=max(ans,dp[i][j]);//max } }printf("%d ",ans); return 0; }
本题与上一题类似,发现样例都一样。
但是神奇的数据范围不得不让我们重新考虑转移方程。
一看每个数都小于等于40,那么很容易想到dp中的一维表示合并的数。
看题解后恍然大悟,设dp[i][j]是能够合并成i且左端点为j的右端点的位置。
那么,dp[i][j]=dp[i-1][dp[i-1][j]]
即,当一个数可以合并成i时,一定是由i-1合并出来的。
我们画一个数轴,其中j在左边。
那么,能合并出i-1的位置就在dp[i-1][j]
那么继续往后,在合并出一个i-1,那么这个位置显然是:
dp[i-1][dp[i-1][j]]
至此,得到转移方程。
那么,表示数字时,我们只需要开到58.
首先,因为数字小于等于40.其次,
262144=218.
用类似倍增的思想,来倍增2.
于是我们要开到58.(也可以用运气法开个大的)
代码:
1 #include<cstdio> 2 #include<iostream> 3 #define MAXN 262144+100 4 using namespace std; 5 int n,a[MAXN],ans; 6 int dp[59][MAXN]; 7 int main(){ 8 scanf("%d",&n); 9 for(int i=1;i<=n;++i)scanf("%d",&a[i]),dp[a[i]][i]=i+1; 10 for(int i=2;i<=58;i++){ 11 for(int j=1;j<=n;j++){ 12 if(!dp[i][j])dp[i][j]=dp[i-1][dp[i-1][j]]; 13 if(dp[i][j])ans=max(ans,i); 14 } 15 }printf("%d ",ans); 16 return 0; 17 }
我们可以先断环为链,然后枚举右端点。相应的,左端点从右端点依次枚举到1.
那么,设dp[i][j]是区间【i,j】合并的最优策略,则有:
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+a[i]*a[k+1]*a[j+1])
我们在i与j之间枚举k做断点,后面部分就是新合并后的价值。
代码:
#include<cstdio> #include<iostream> using namespace std; struct node{ int l,r;//l from r to }q[300]; int n,a[300],maxn; int dp[300][300]; int main(){ scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]),a[i+n]=a[i]; for(int j=2;j<=(n<<1);++j){//枚举右端点 for(int i=j-1;j-i<n&&i>=1;i--){//向左依次枚举区间 for(int k=i;k<j;++k){//枚举断点 dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+a[i]*a[k+1]*a[j+1]); maxn=max(dp[i][j],maxn);//每次合并=max(左区间值+右区间值+新合并的值) }//取max } } printf("%d ",maxn); return 0; }