题目
题目描述
丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共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,按顺序给出圈中的数字,首尾相接。
输出
输出文件有两行,各包含一个非负整数。第一行是你程序得到的最小值,第二行是最大值。
样例输入
4 2
4
3
-1
2
样例输出
7
81
题目大意
输入n,m和n个整数。要求把这n个整数环形排列后分成m个部分,求每个部分的和mod10后乘起来的积最大或最小
算法
这道题是个人都知道用动态规划。我们设表示把区间(
)分成k份的最小乘积,
表示把区间(
)分成k份的最大乘积。 设
表示第
个元素的前缀和。则
=
-
。因为把一段元素分成一份,它的最大值也是唯一值就是该份的元素的总和。
我们可以把的某段元素提取出来,加到第
份中。假设我们把第
个元素提取出来,则
=min{
×
-
-
}(
),即把第
到
的元素全部加到第k份里之所以可以这样,是因为丁丁只让我们选取一段连续的元素作为一个部分。
最后我们只要输出min{}(
)即可。
g数组同理,怎么改留作思考。
注意:要把a数组复制一遍,变成环。
代码
#include <cstdio>
#define ll long long
using namespace std;
const ll INF=0x7ffffffffffffff/3;
const int N=105,M=15;
ll a[N],s[N],f[N][N][M],g[N][N][M];//不用long long会WA掉的。
ll max(ll x,ll y)
{
if (x>y) return x;
return y;
}
ll min(ll x,ll y)
{
if (x<y) return x;
return y;
}
ll mod(ll x)//方面mod10
{
return (x%10+10)%10;
}
int main()
{
int n,m;
ll ans1=INF,ans2=-INF;
scanf("%d %d",&n,&m);
for (int i=1; i<=n; i++)
{
scanf("%lld",&a[i]);
a[n+i]=a[i];//复制成环
}
s[0]=0;
for (int i=1; i<=2*n; i++) s[i]=s[i-1]+a[i];
for (int i=1; i<=2*n; i++)
for (int j=1; j<=2*n; j++)
{
f[i][j][1]=mod(s[j]-s[i-1]);//初始化fi,j,1
g[i][j][1]=mod(s[j]-s[i-1]);//初始化gi,j,1
}
for(int i=1;i<=2*n;i++)
for(int j=i;j<=2*n;j++)
for(int k=2;k<=m;k++)
f[i][j][k]=INF; //因为f存的是最小值,所以要把它初始化为∞。
for (int k=2; k<=m; k++)
for (int i=k; i<=n; i++)
for (int j=1; j<=2*n-i+1; j++)
{
for (int u=j; u<j+i-1; u++)
{
f[j][j+i-1][k]=min(f[j][j+i-1][k],f[j][u][k-1]*mod(s[j+i-1]-s[u]));
g[j][j+i-1][k]=max(g[j][j+i-1][k],g[j][u][k-1]*mod(s[j+i-1]-s[u]));
}
}
for (int i=1; i<=n; i++)
{
ans1=min(ans1,f[i][i+n-1][m]);//求出min{fi,i+n-1,m}
ans2=max(ans2,g[i][i+n-1][m]);
}
printf("%lld\n%lld",ans1,ans2);
return 0;
}