洛谷P1043数字游戏题解--zhengjun

题目描述

丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共 n n n个),你要按顺序将其分为 m m m个部分,各部分内的数字相加,相加所得的 m m m个结果对 10 10 10取模后再相乘,最终得到一个数 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行每行有个整数,其绝对值 ≤ 1 0 4 \le 10^4 104,按顺序给出圈中的数字,首尾相接。

输出格式

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

输入输出样例
输入 #1 复制
4 2
4
3
-1
2
输出 #1 复制
7
81

思路

这题是一个典型的区间dp。

为了让一个环变成一条链,就可以把换剪开,在剪开的地方添加剪开前相连的数,然后就是把原数组翻了两倍,这样做dp就可以了,不过还要注意一下最终的答案要重新找一下。

f i , j , k f_{i,j,k} fi,j,k表示在 i i i j j j的区间中分成 k k k份所得的最大(最小)值。

转移公式:

f i , j , k = max ⁡ / min ⁡ { f i , t , k − 1 + s u m t + 1 , j   m o d   10 } f_{i,j,k}=\max/\min\{f_{i,t,k-1}+sum_{t+1,j}\bmod10\} fi,j,k=max/min{fi,t,k1+sumt+1,jmod10}

i + k − 2 ≤ t < j , s u m i , j i+k-2\leq t<j,sum_{i,j} i+k2t<j,sumi,j表示从 i i i j j j段的总和

这个 s u m sum sum可以用前缀和处理出来

代码

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
int n,m;
int minf[101][101][10],maxf[101][101][10];
int a[101];
int mod(int x){
	return ((x%10)+10)%10;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
    	scanf("%d",&a[i]);
    	a[i+n]=a[i];//翻两倍
	}
	for(int i=2;i<=2*n;i++)a[i]+=a[i-1];//前缀和
	for(int i=1;i<=2*n;i++)
	    for(int j=i+1;j<=2*n;j++)
	        for(int k=2;k<=j-i+1;k++)
	            minf[i][j][k]=0x7fffffff;//初始值
	for(int i=1;i<=2*n;i++)
	    for(int j=i;j<=2*n;j++)
	        minf[i][j][1]=maxf[i][j][1]=mod(a[j]-a[i-1]);//不分的时候就是这段的和mod 10
	for(int i=2;i<=m;i++){//枚举分的段数
		for(int j=1;j<=2*n;j++){//枚举左端点
			for(int k=j+i-1;k<=2*n;k++){//枚举右端点
				for(int l=j+i-1-1;l<=k-1;l++){//枚举从哪里切一刀
					minf[j][k][i]=min(minf[j][k][i],minf[j][l][i-1]*mod(a[k]-a[l]));//注意这里的l已经是sum的左端点-1了
					maxf[j][k][i]=max(maxf[j][k][i],maxf[j][l][i-1]*mod(a[k]-a[l]));
				}
			}
		}
	}
	int minans=0x7fffffff,maxans=0;
	for(int i=1;i<=n;i++){//寻找答案
		minans=min(minans,minf[i][i+n-1][m]);
		maxans=max(maxans,maxf[i][i+n-1][m]);
	}
	printf("%d\n%d",minans,maxans);//输出
	return 0;
}

谢谢–zhengjun

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
过河卒是一个典型的动态规划问题。首先,我们将整个棋盘看作一个二维数组,数组的每个元素表示到达该位置的路径数目。然后,我们根据题目给出的条件,逐步更新数组的元素,直到计算出到达目标位置的路径数目。 具体的解题思路如下: 1. 首先,我们可以将马的位置设置为0,表示无法经过该位置。 2. 然后,我们根据马的位置,更新数组的元素。对于二维数组的每个位置,我们根据左边和上边的位置来计算到达当前位置的路径数目。具体地,如果左边和上边的位置都可以经过,那么到达当前位置的路径数目就等于左边和上边位置的路径数目之和。如果左边或上边的位置无法经过,那么到达当前位置的路径数目就等于左边或上边位置的路径数目。 3. 最后,我们输出目标位置的路径数目。 下面是洛谷p1002过河卒题解C++代码: ```cpp #include <bits/stdc++.h> using namespace std; int main() { long long a[21][21]; int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; // 初始化数组,马的位置设置为0 for(int i=0; i<=20; i++) { for(int k=0; k<=20; k++) { a[i][k] = 1; } } a[x2][y2] = 0; // 根据马的位置更新数组的元素 if(x2 >= 2 && y2 >= 1) a[x2-2][y2-1] = 0; if(x2 >= 1 && y2 >= 2) a[x2-1][y2-2] = 0; if(x2 <= 18 && y2 >= 1) a[x2+2][y2-1] = 0; if(x2 <= 19 && y2 >= 2) a[x2+1][y2-2] = 0; if(x2 >= 2) a[x2-2][y2+1] = 0; if(x2 >= 1) a[x2-1][y2+2] = 0; if(y2 >= 1) a[x2+2][y2-1] = 0; if(y2 >= 2) a[x2+1][y2-2] = 0; // 动态规划计算路径数目 for(int i=1; i<=20; i++) { for(int k=1; k<=20; k++) { if(a[i][k] != 0) { a[i][k] = a[i-1][k] + a[i][k-1]; } } } // 输出目标位置的路径数目 cout << a[x1][y1] << endl; return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A_zjzj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值