P1775
典。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e2+5;
int n,a[N],sum[N],dp[N][N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
memset(dp,0x3f,sizeof dp);
for(int i=1;i<=n;i++)
cin>>a[i],sum[i]=sum[i-1]+a[i],dp[i][i]=0;
for(int l=2;l<=n;l++){
for(int i=1;i+l-1<=n;i++){
int j=i+l-1;
for(int k=i;k<j;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
dp[i][j]+=sum[j]-sum[i-1];
}
}
cout<<dp[1][n];
return 0;
}
P1040
很容易发现这玩意是个区间 dp。因为中序遍历是 左-根-右 的,考虑枚举根。
状态:令 \(dp_{i,j}\) 表示区间 \([i,j]\) 的最高加分。
初始:\(dp_{i,i}=a_i,dp_{i,i-1}=1\)。
转移:\(dp_{i,j}=dp_{i,k} \times dp_{k+1,j} + a_k\)。
答案:\(dp_{1,n}\)。
每次转移成功时顺便记录根即可。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=35;
int n,a[N],dp[N][N],rt[N][N];
void dfs(int l,int r){
if(l>r)
return;
cout<<rt[l][r]<<' ';
dfs(l,rt[l][r]-1);
dfs(rt[l][r]+1,r);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i],dp[i][i]=a[i],dp[i][i-1]=1,rt[i][i]=i;
for(int l=2;l<=n;l++){
for(int i=1;i+l-1<=n;i++){
int j=i+l-1;
for(int k=i;k<j;k++)
if(dp[i][j]<dp[i][k-1]*dp[k+1][j]+a[k])
dp[i][j]=dp[i][k-1]*dp[k+1][j]+a[k],rt[i][j]=k;
}
}
cout<<dp[1][n]<<'\n';
dfs(1,n);
return 0;
}