蓝桥杯刷题——邮票面值设计(动态规划+深度优先搜索)

270 阅读1分钟

「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。

这是蓝桥杯算法提高中的一道题,是动态规划和深度优先搜索的联合运用,有一定难度。

题目

一封信上只能贴n个邮票,共有k种不同面值的邮票(k+n<=13),要求我们算出这些邮票的值,这些邮票的值要满足贴在信封上有1到MAX连续的值,要求所求出的邮票值令MAX最大。比如 n=3,k=2,信上只能贴三个邮票,邮票有两种,当邮票面值为1,3时,MAX最大是7,一个1是1,两个1是2,1个3是3,一个3一个1是4,两个1一个3是5,两个3是6,两个3一个1是7,这里是连续的。输出邮票值以及MAX。

思路

其实解题脉络很清晰,先确定邮票的面值,然后再去算这些邮票所能达到的最大连续值,遍历所有存在的情况。我们如何确定邮票面值呢?邮票面值是不同的,我们将邮票面值从小到大排好,首先,第一张邮票面值一定是1,第二张邮票面值的范围我们如何确定?是2到无限大吗?不是,第二张邮票的最大值是n乘以第一张邮票面值+1,为什么是这样?如果第二张面值大于n乘以第一张邮票面值加一,那么所算出的最大连续值就是n了,只要有第二张邮票就一定会出现断层,这样的情况我们直接去除,可以大大降低我们的运算量。而第三张邮票面值就是在第二张邮票面值的基础去确定,大于第二张邮票面值但小于n乘以第二张邮票面值+1,后面的邮票面值也是这样,以此类推。用dfs遍历每种邮票 面值情况,每次遍历后,我们再进行动态规划,来看看最大连续值是多少。确定dp数组dp[i],dp[0]=0,表示连续值是0的时候,不需要选择邮票上去,dp[1]=1,表示连续值是1的时候,需要一张邮票。用value[]数组表示邮票面值,value[0]就是第一张邮票,可以得到状态转移公式,dp[i]=dp[i-value[i]]+1。最后只需找出最大连续值的那个邮票组合即可。

总代码

#include<bits/stdc++.h>
using namespace std;
int dp[200];
int value[13];
int result[13];
int n,m;
int MAX;
void DP(){
	memset(dp,0,sizeof(dp));
	dp[0]=0;
	dp[1]=1;
	for(int i=2;i<=value[m-1]*n;i++){
		int min=100000;
		for(int j=0;j<m;j++){
			if(i-value[j]>=0){
				min=dp[i-value[j]]+1;
				dp[i]=min;
			}
		}
		if(dp[i]>n){//不符合条件了直接退出。 
			break;
		}
		if(i>MAX){
			MAX=i;//
			for(int k=0;k<m;k++){
				result[k]=value[k];//保留当前最优答案。 
			} 
		} 
	}
}
void dfs(int x){
	if(x==m){//邮票已经全部赋好值了,开始用dp判断 
		DP(); 
	}
	else if(x<m)
		for(int i=value[x-1]+1;i<=n*value[x-1]+1;i++){
			value[x]=i;
			dfs(x+1);
		}
}

int main(){
	cin>>n>>m;
	value[0]=1;
	dfs(1);
	for(int i=0;i<m;i++){
		cout<<result[i]<<' ';
	}
	cout<<endl;
	cout<<MAX;
	return 0;
}