C++-数字游戏(NOIP 2003 PJT2) 解题思路
【Horn Studio】编程专栏:数字游戏(NOIP 2003 PJT2)解题思路
题目
题目描述
丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共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行每行有个整数,其绝对值不大于104,按顺序给出圈中的数字,首尾相接。保证答案在int范围内。
输出
输出文件有两行,各包含一个非负整数。第一行是你程序得到的最小值,第二行是最大值。
样例输入 复制
4 2
4
3
-1
2
样例输出 复制
7
81
提示
来源
思路
这道题是一道区间DP题,跟能量项链还有石子合并差不多,我们用f[i][j][l]表示处理i到j这一区间分为l段所能得到的最大值或是最小值。
对于DP,只要写出了公式,之后就是打打代码的事情。
简单来说,不用枚举两侧分成了多少个部分,因为这会被之前或之后的循环枚举到,
并且我们同时需要最大值和最小值,只需写两个完全相反的代码。
for(int k=i;k<j;k++) { f[i][j][l]=max(f[i][j][l],f[i][k][l-1]*total[k+1][j]); if(g[i][k][l-1]<0x3F3F3F3F) g[i][j][l]=min(g[i][j][l],g[i][k][l-1]*total[k+1][j]); }
这道题的min数组需要注意一下,必须要写0x3F3F3F3F,这就是无穷大,也不用担心会溢出。
代码
#include<bits/stdc++.h> using namespace std; int n,m,a[130],f[130][130][100],g[130][130][100],sum[130]; int minans=0x3F3F3F3F,maxans=-1,total[100][100]; int main(){ cin>>n>>m; for(int i=1;i<=n;++i) { cin>>a[i]; a[i+n]=a[i]; } n<<=1; memset(g,0x3F3F3F3F,sizeof(g)); for(int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i]; for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) f[i][j][1]=g[i][j][1]=total[i][j]=((sum[j]-sum[i-1])%10+10)%10; for(int l=2;l<=m;l++) for(int j=1;j<=n;j++) for(int i=1;i<=j;i++) for(int k=i;k<j;k++) { f[i][j][l]=max(f[i][j][l],f[i][k][l-1]*total[k+1][j]); if(g[i][k][l-1]<0x3F3F3F3F) g[i][j][l]=min(g[i][j][l],g[i][k][l-1]*total[k+1][j]); } n>>=1; for(int i=1;i<=n;++i){ maxans=max(maxans,f[i][i+n-1][m]); minans=min(minans,g[i][i+n-1][m]); } cout<< minans<<endl<<maxans; }
彩蛋
哈嗨嗨!