洛谷P1043 数字游戏
题目描述:
传送门:https://www.luogu.com.cn/problem/P1043
解题思路:
由于是一个环,考虑将数组抄两遍
由于是区间和相乘,考虑用前缀和记录
由于是一个区间DP,考虑状态转移方程:
设 f [i][j][k] 为区间 [i,j] 中有 k 个分段的最大值或最小值(设 ffmax为最大值,ffmin为最小值)
先枚举k,即目前已经被分为 k 个区间,再枚举区间的左端点和右端点,将状态分为两段:前k-1 段的最值+这一段的最值。枚举这个分界线在哪里,从而列出状态转移方程
由于要初始化,将 f[i][j][1] 赋值为sum[j] - sum[i-1] ,表示把点i到点j之间的数只分成一段的值
代码实现:
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; inline int get() { char c; int sign=1; while((c=getchar())<'0'||c>'9') { if(c=='-') { sign=-1; } } int res=c-'0'; while((c=getchar())>='0'&&c<='9') { res=res*10+c-'0'; } return res*sign; } const int N=51,mod=10,M=10; int n,m,ansmax=-1,ansmin=100000000; int a[2*N],ffmax[2*N][2*N][M],ffmin[2*N][2*N][M],sum[2*N]; int main() { n=get(),m=get(); for(int i=1;i<=n;i++){ a[i]=get(); a[i+n]=a[i]; } for(int i=1;i<=n*2;i++){ sum[i]=sum[i-1]+a[i]; } memset(ffmin,0x3f3f,sizeof(ffmin)); memset(ffmax,-1,sizeof(ffmax)); for(int i=1;i<=2*n;i++){ for(int j=i;j<=2*n;j++){ ffmax[i][j][1]=((sum[j]-sum[i-1])%mod+mod)%mod; ffmin[i][j][1]=((sum[j]-sum[i-1])%mod+mod)%mod; } } for(int i=2;i<=m;i++){ for(int l=1;l<=n;l++){ for(int r=l+i-1;r<=l+n-1;r++){ for(int d=l+i-2;d<=r-1;d++){ ffmax[l][r][i]=max(ffmax[l][r][i],ffmax[l][d][i-1]*(((sum[r]-sum[d+1-1])%mod+mod)%mod)); ffmin[l][r][i]=min(ffmin[l][r][i],ffmin[l][d][i-1]*(((sum[r]-sum[d+1-1])%mod+mod)%mod)); } } } } for(int i=1;i<=n;i++){ ansmax=max(ansmax,ffmax[i][i+n-1][m]); ansmin=min(ansmin,ffmin[i][i+n-1][m]); } printf("%d\n%d",ansmin,ansmax); return 0; }
再见,祝你好运!!