【NOIP2003PJ】数字游戏 题解

题目

题目描述

丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共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后乘起来的积最大或最小

算法

        这道题是个人都知道用动态规划。我们设f_{i},_{j},_{k}表示把区间(i,j)分成k份的最小乘积,g_{i},_{j},_{k}表示把区间(i,j)分成k份的最大乘积。 设s_{i}表示第i个元素的前缀和。则f_{i},_{j},_{1}=s_{j}-s_{i-1}。因为把一段元素分成一份,它的最大值也是唯一值就是该份的元素的总和。

        我们可以把f_{j},_{j+i-1},_{k-1}的某段元素提取出来,加到第k份中。假设我们把第u个元素提取出来,则

f_{j},_{j+i-1},_{k}=min{f_{j},_{u},_{k-1}×s_{j+i-1}-s_{u}-s_{u}}(j\leqslant u< i+j-1),即把第ji+j-1的元素全部加到第k份里之所以可以这样,是因为丁丁只让我们选取一段连续的元素作为一个部分。

         最后我们只要输出min{f_{i},_{i+j-1},_{m}}(1\leqslant i\leqslant n)即可。

        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;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值