石子合并
分析:dp[i][j]表示从i顺时针数j个位置的最大值,规划方向是顺推,初始时dp[i][i]=0。显然,我们需要求出合并个数为2,3,,,,n的情况,对于dp[i][j]我们假设最后一次合并位置为k,dp[i][j]=dp[i][k]+dp[k+1][j]+sum[i,j],因为sum[i,j]表示i到j这段和,跟k取的位置无关,由此可以得到dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]),最大值同理
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 using namespace std; 6 const int maxn=200+10; 7 const int INF=1<<30; 8 int f[maxn][maxn]; //max 9 int dp[maxn][maxn]; //min 10 int sum[maxn],a[maxn]; 11 int n; 12 int main() 13 { 14 cin>>n; 15 for(int i=1;i<=n;i++){ 16 cin>>a[i]; 17 sum[i]=sum[i-1]+a[i]; 18 } 19 for(int i=n+1;i<=2*n;i++) 20 sum[i]=sum[n]+sum[i-n]; 21 for(int i=1;i<=2*n;i++) 22 for(int j=1;j<=2*n;j++) 23 dp[i][j]=INF; 24 for(int i=1;i<=2*n;i++) dp[i][i]=0; 25 for(int l=1;l<=n;l++){ 26 for(int i=1;i<=2*n-l;i++){ 27 int j=i+l; 28 for(int k=i;k<j;k++){ 29 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); 30 } 31 } 32 } 33 memset(f,0,sizeof(f)); 34 for(int l=1;l<=n;l++){ 35 for(int i=1;i<=2*n-l;i++){ 36 int j=i+l; 37 for(int k=i;k<j;k++){ 38 f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]); 39 } 40 } 41 } 42 int cnt=0,ans=INF; 43 for(int i=1;i<=n;i++) 44 cnt=max(f[i][i+n-1],cnt); 45 for(int i=1;i<=n;i++) 46 ans=min(dp[i][i+n-1],ans); 47 cout<<ans<<endl<<cnt<<endl; 48 }
能量项链
分析:dp[i][j]表示i到j这段区间的最大值,我们需要枚举从哪个点切分,但是注意一个问题,对于一次获得能量,我们所需要用到的是a[i]和a[i+1]的值,所以可以得到状态转移方程dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+(a[i]*a[k+1]*a[j+1]))
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 using namespace std; 6 const int maxn=200+10; 7 int a[maxn]; 8 int dp[maxn][maxn]; 9 int n; 10 int main() 11 { 12 cin>>n; 13 for(int i=1;i<=n;i++){ 14 cin>>a[i]; 15 a[i+n]=a[i]; 16 } 17 for(int l=1;l<=n;l++){ 18 for(int i=1;i<=2*n-l;i++){ 19 int j=i+l; 20 for(int k=i;k<j;k++){ 21 dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+(a[i]*a[k+1]*a[j+1])); 22 } 23 } 24 } 25 int cnt=0; 26 for(int i=1;i<=n;i++) 27 cnt=max(cnt,dp[i][i+n-1]); 28 cout<<cnt<<endl; 29 }
数字游戏
分析:dp[i][j][z]表示区间[i,j]被分成z段的最大值,故dp[i][j][z]=max(dp[i][j][z],dp[i][k][z-1]*dp[k+1][j][1]),最小值同理
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 using namespace std; 6 const int maxn=100+10; 7 const int INF=1<<30; 8 int n,m; 9 int a[maxn],sum[maxn]; 10 int dp[maxn][maxn][20]; //max 11 int f[maxn][maxn][20]; //min 12 int main() 13 { 14 cin>>n>>m; 15 for(int i=1;i<=n;i++){ 16 cin>>a[i]; 17 sum[i]=sum[i-1]+a[i]; 18 } 19 for(int i=n+1;i<=2*n;i++) 20 sum[i]=sum[n]+sum[i-n]; 21 memset(dp,0,sizeof(dp)); 22 for(int i=1;i<=2*n;i++) 23 for(int j=1;j<=2*n;j++) 24 for(int z=1;z<=m;z++) 25 f[i][j][z]=INF; 26 for(int i=1;i<=2*n;i++){ 27 for(int j=1;j<=2*n;j++){ 28 dp[i][j][1]=sum[j]-sum[i-1]; 29 f[i][j][1]=sum[j]-sum[i-1]; 30 while(dp[i][j][1]<0) dp[i][j][1]+=10; 31 dp[i][j][1]%=10; 32 while(f[i][j][1]<0) f[i][j][1]+=10; 33 f[i][j][1]%=10; 34 } 35 } 36 for(int l=1;l<=n;l++){ 37 for(int i=1;i<=2*n-l;i++){ 38 int j=i+l; 39 for(int k=i;k<j;k++){ 40 for(int z=2;z<=m;z++) 41 dp[i][j][z]=max(dp[i][j][z],dp[i][k][z-1]*dp[k+1][j][1]); 42 } 43 } 44 } 45 for(int i=1;i<=2*n;i++) 46 for(int z=2;z<=m;z++) 47 f[i][i][z]=0; 48 for(int l=1;l<=n;l++){ 49 for(int i=1;i<=2*n-l;i++){ 50 int j=i+l; 51 for(int k=i;k<j;k++){ 52 for(int z=2;z<=m;z++) 53 f[i][j][z]=min(f[i][j][z],f[i][k][z-1]*f[k+1][j][1]); 54 } 55 } 56 } 57 int cnt=0,ans=INF; 58 for(int i=1;i<=n;i++) 59 cnt=max(dp[i][i+n-1][m],cnt); 60 for(int i=1;i<=n;i++) 61 ans=min(f[i][i+n-1][m],ans); 62 cout<<ans<<endl<<cnt<<endl; 63 }