专栏文章

P1043 [NOIP 2003 普及组] 数字游戏 C++题解

P1043题解参与者 2已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@mipg2xaa
此快照首次捕获于
2025/12/03 11:25
3 个月前
此快照最后确认于
2025/12/03 11:25
3 个月前
查看原文

题意


有一个圆圈,在圆圈上首尾相接的放着 nn 个数,现在让你按顺序将这些数分成 mm 个部分,把每个部分内的数字相加,将这 mm 个结果相乘并模 1010,得到最终结果 kk,现在给你 n,mn,mnn 个整数,求最大和最小的 kk

分析


看到题目,先想到了 DFS,但是对于题目的数据范围会直接爆炸。可以使用动态规划,DP 的方法来做这道题。
但是题目中的数字是一条环,怎么办呢?我们可以把这条环拉直,形成一条长为 2×n2 \times n。那我们令 dpi,j,lendp_{i,j,len} 为从第 ii 个数开始,第 jj 个数结束,分成了 lenlen 段。当然,结合题目要求,我们把 dpdp 数组拆成 MaxMax 数组和 MinMin 数组(MaxMax 表示最大,MinMin 表示最小)。我们在枚举 i,j,leni,j,len 后还要枚举一个变量 kk,表示我们在哪里分割。所以动态转移方程为:
Mini,j,len=min{Mini,j,len,Mini,k,len1×Mink+1,j,1}Min_{i,j,len}=\min\{Min_{i,j,len},Min_{i,k,len-1} \times Min_{k+1,j,1}\}
边界条件呢?当 len=1len=1 时,只分成一段,也就是 dpi,j,1=k=jiak mod10dp_{i,j,1}=\sum_{k=j}^{i}a_k \ \bmod 10

代码


下面给出代码。
Code:
CPP
//The Code is From @Noah03(Luogu User Name),Do Not Copy!!!
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[60],s[110],Max[110][110][15],Min[110][110][15];
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		s[i]=s[i-1]+a[i]; //前缀和
	}
	for(int i=n+1;i<=(n<<1);i++) s[i]=s[i-1]+a[i-n]; //化环为链
	for(int i=1;i<=110;i++) for(int j=1;j<=110;j++) for(int k=1;k<=10;k++) Min[i][j][k]=1e12; //初始化 
	for(int i=1;i<=(n<<1);i++)
		for(int j=1;j<=(n<<1);j++)
			Max[i][j][1]=Min[i][j][1]=(s[j]-s[i-1]+1000000000)%10;
    //负数取模可能会出现负数,先加上一个极大值
	for(int i=1;i<=(n<<1);i++)
		for(int j=i+1;j<=(n<<1);j++)
			for(int len=2;len<=m;len++)
				for(int k=i;k<j;k++){
					Max[i][j][len]=max(
					Max[i][j][len],
					Max[i][k][len-1]*
					Max[k+1][j][1]);
					Min[i][j][len]=min(
					Min[i][j][len],
					Min[i][k][len-1]*
					Min[k+1][j][1]);
                    //动态转移方程
				}
	ll ans1=0,ans2=1e18;
	for(int i=1;i<=n;i++){
		ans1=max(ans1,Max[i][i+n-1][m]); //取最大值
		ans2=min(ans2,Min[i][i+n-1][m]); //取最小值
	}
	printf("%lld\n%lld\n",ans2,ans1);
	return 0; //结束
}

评论

1 条评论,欢迎与作者交流。

正在加载评论...