题目描述
丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k。游戏的要求是使你所得的k最大或者最小。
例如,对于下面这圈数字(n=4,m=2):
要求最小值时,((2-1) mod 10)×((4+3) mod 10)=1×7=7,要求最大值时,为((2+4+3) mod 10)×(-1 mod 10)=9×9=81。特别值得注意的是,无论是负数还是正数,对10取模的结果均为非负值。
丁丁请你编写程序帮他赢得这个游戏。
输入输出格式
输入格式:输入文件第一行有两个整数,n(1≤n≤50)和m(1≤m≤9)。以下n行每行有个整数,其绝对值不大于10410^4104 ,按顺序给出圈中的数字,首尾相接。
输出格式:输出文件有两行,各包含一个非负整数。第一行是你程序得到的最小值,第二行是最大值。
输入输出样例
分析
明显的区间环形dp;
做法
做法
先跟[NOI2001合并石子]一样,复制一下序列,断环成链。 然后设dp[l,r,i] 为将区间[l,r] 分成i 个部分相乘的最优解,可以得出如下状态转移方程 dp[l,r,k]=max{dp[l,k,i-1]*(sum-sum[k]),dp[l,r,k]} sum 用前缀和来维护。
卡点
注意一下mod 运算和取余运算不同,如果为负数则需要变成正数。
卡点
注意一下mod 运算和取余运算不同,如果为负数则需要变成正数。
#include<cstdio>
#include<algorithm>
using namespace std;
int f[2][20][105][105];
int n,m;
int sum[105];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&sum[i]);
sum[i+n]=sum[i];
}
for(int i=1;i<=n<<1;i++)
sum[i]+=sum[i-1];
for(int i=1;i<=n;i++)
for(int j=i;j<=i+n-1;j++)
f[1][1][i][j]=f[0][1][i][j]=((sum[j]-sum[i-1])%10+10)%10;
for(int i=2;i<=m;i++)
for(int l=1;l<=n;l++)
for(int r=l+i-1;r<l+n;r++){
f[0][i][l][r]=1e16;
for(int k=i+l-2;k<r;k++){
int t=((sum[r]-sum[k])%10+10)%10;
f[1][i][l][r]=max(f[1][i][l][r],f[1][i-1][l][k]*t);
f[0][i][l][r]=min(f[0][i][l][r],f[0][i-1][l][k]*t);
}
}
int ans1=1e16,ans2=-1e16;
for(int i=1;i<=n;i++){
ans1=min(f[0][m][i][i+n-1],ans1);
ans2=max(f[1][m][i][i+n-1],ans2);
}
printf("%d\n%d\n",ans1,ans2);
return 0;
}