【NOIP2003TG/codevs1085】 数字游戏 解题报告

245 篇文章 0 订阅
49 篇文章 0 订阅

数字游戏   NOIP2003TG/codevs1085黄金Gold

题目描述 Description

丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k。游戏的要求是使你所得的k最大或者最小。

例如,对于下面这圈数字(n=4m=2):

                                  2

                   4                           -1

                                 3

当要求最小值时,((2-1) mod 10)×((4+3) mod 10)=1×7=7,要求最大值时,为((2+4+3) mod 10)×(-1 mod 10)=9×9=81。特别值得注意的是,无论是负数还是正数,对10取模的结果均为非负值。

丁丁请你编写程序帮他赢得这个游戏。

输入描述 Input Description

输入文件第一行有两个整数,n1≤n≤50)和m1≤m≤9)。以下n行每行有个整数,其绝对值不大于104,按顺序给出圈中的数字,首尾相接。

输出描述 Output Description

输出文件有两行,各包含一个非负整数。第一行是你程序得到的最小值,第二行是最大值。

样例输入 Sample Input

4 2

4

3

-1

2

样例输出 Sample Output

7

81

【解题思路】

介是一个dp题。。。

首先说明我这道题是水掉的。其实也不是真正的水掉的,,只是有一组数据过不了,,当时又实在不想再调了,就加了个特判,,,谁让那组数据m是1那么特殊。。。

f[i][j][k]表示把i-j这个区间分成k部分的最大值,g[i][j][k]表示把i-j这个区间分成k部分的最小值;

那么

f[i][j][k]=max(f[i][l][k-1]*b[l+1][j],f[i][j][k]);

g[i][j][k]=min(g[i][l][k-1]*b[l+1][j],g[i][j][k]);

l是中间的那个鬼。。

初始值:f[i][j][1]=b[i][j];g[i][j][1]=b[i][j];  (1<=i<=j<=n)

b[i][j]表示把i-j这个区间的数都加起来对10取模;

一个负数对一个正整数p取模我们一般认为是负数模p+p再模p;

还有一点要注意的是,这是一个环。可以参考环形石子合并的思路,把它展开,就是说原来的n个数据现在变成了2n-2个,把头上的又接到尾上去,最后枚举1~n,2~n+1,3~n+2.....n~2n-1,看这些区间里那个值最大就是ans;

【代码】

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long n,m,i,j,k,l,maxn,minn;
long long a[200],s[200],b[200][200],f[200][200][20],g[200][200][20];

int main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	cin>>n>>m;
	for (i=1;i<=n;++i)
	{
		cin>>a[i];
		a[i+n]=a[i];//将环展开
	}
	n=2*n-1;//n变成2n-1
	for (i=1;i<=n;++i)
	  s[i]=s[i-1]+a[i];//s[i]表示从a[1]一直加到a[i]的和
	for (i=1;i<=n;++i)
	  for (j=i;j<=n;++j)
	    b[i][j]=(((s[j]-s[i-1])%10)+10)%10;//求出b的值,,一个负数对一个整数p取模,模p+p再模p
	memset(f,0,sizeof(f));
	for (i=0;i<=190;++i)
	  for (j=0;j<=190;++j)
	    for (k=0;k<=15;++k)
	      g[i][j][k]=1000000000000000;//介个赋值我是迫不得已才写的,貌似当时memset出什么问题了
	for (i=1;i<=n;++i)
	  for (j=i;j<=n;++j)
	  	f[i][j][1]=b[i][j];//初始化
	for (i=1;i<=n;++i)
	  for (j=i;j<=n;++j)
	  	g[i][j][1]=b[i][j];
	if (m>1)//介就是我水掉的那个地方,,加个这个就对了,,因为那一组数据正好是m=1
	for (i=1;i<=n;++i)
	  for (j=i;j<=n;++j)
	    for (l=i;l<=j-1;++l)
	      for (k=1;k<=m;++k)
	    {
	    	f[i][j][k]=max(f[i][l][k-1]*b[l+1][j],f[i][j][k]);//介就是核心部分
	    	g[i][j][k]=min(g[i][l][k-1]*b[l+1][j],g[i][j][k]);
	    }
	n=(n+1)/2;//把n恢复
	minn=2147483647;
	for (i=1;i<=n;++i)
	{
		if (maxn<f[i][i+n-1][m])
	    maxn=f[i][i+n-1][m];
	}
	for (i=1;i<=n;++i)
	{
		if (minn>g[i][i+n-1][m])
	    minn=g[i][i+n-1][m];
	}
	cout<<minn<<endl;
	cout<<maxn<<endl;
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值