数字游戏

数字游戏 ⁡ \operatorname{数字游戏}

题目链接: luogu P1043 ⁡ \operatorname{luogu\ P1043} luogu P1043

题目

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

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

在这里插入图片描述

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

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

输入

输入文件第一行有两个整数, n ( 1 ≤ n ≤ 50 ) n(1≤n≤50) n(1n50) m ( 1 ≤ m ≤ 9 ) m(1≤m≤9) m(1m9) 。以下 n n n 行每行有个整数,其绝对值 $\le 10^4 ,按顺序给出圈中的数字,首尾相接。

输出

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

样例输入

4 2
4
3
-1
2

样例输出

7
81

思路

这道题是一道 dp 。

我们先看求最大:设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 为第 i i i 个数到第 j j j 个数,分成 k k k 个部分最大的 k k k 是多少。那就是从分成 k − 1 k - 1 k1 和一个部分合在一起转移过来。那就枚举四重循环,前三个就是 i j k ijk ijk ,在枚举一个表示从哪里割开两半。
那我们可以看出当第三维是 1 1 1 的时候,是转移不了的,那就要用初始化。因为只分一份,就直接相加取模,而相加可以用前缀和来所短时间。
而找到最佳答案时,要枚举每一种割开环的情况,找到最大的答案。

求最小的同理。

要注意的就是题目负数的取模不一样,正常取模之后加 10 10 10 在取模一次就可以了。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

ll n, m, a[101], minn[101][101][10], maxn[101][101][10], q[101], maxans, minans = 10000000000000000;

int main() {
	for (int i = 1; i <= 100; i++)
		for (int j = 1; j <= 100; j++)
			for (int k = 1; k <= 10;k++)
				minn[i][j][k] = 1000000000000;//初始化
	
	scanf("%lld %lld", &n, &m);//读入
	for (ll i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		a[i + n] = a[i];//把环切开
		
		q[i] = q[i - 1] + a[i];//前缀和
	}
	for (ll i = n + 1; i <= 2 * n; i++)
		q[i] = q[i - 1] + a[i];//前缀和
	
	for (ll i = 1; i <= n * 2; i++)
		for (ll j = 1; j <= n * 2; j++)
			maxn[i][j][1] = minn[i][j][1] = ((q[j] - q[i - 1]) % 10 + 10) % 10;//预处理
	
	for (ll i = 1; i <= n * 2; i++)
		for (ll j = i + 1; j <= n * 2; j++)
			for (ll k = 2; k <= m; k++)
				for (ll l = i; l < j; l++) {//dp
					maxn[i][j][k] = max(maxn[i][j][k], maxn[i][l][k - 1] * maxn[l + 1][j][1]);
					minn[i][j][k] = min(minn[i][j][k], minn[i][l][k - 1] * minn[l + 1][j][1]);
				}
	
	for (ll i = 1; i <= n; i++) {//找到最好的起点
		maxans = max(maxans, maxn[i][i + n - 1][m]);
		minans = min(minans, minn[i][i + n - 1][m]);
	}
	
	printf("%lld\n%lld", minans, maxans);//输出
	
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值