「这是我参与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;
}