【洛谷】P1021 邮票面值设计
0.总结
Get to the points first. The article comes from LawsonAbs!
- dfs+dp;
- 注意这种问题
dp
数组含义的令法很关键,我数次将这个dp[i]
令成价值i是否可达。而真正的应该设成:到达价值i
的最少需要的票数; - 在
dfs
中的dp
数组需要置零,因为是根据每次的选择都要重新计算;
1.题意
给出邮票数限制,邮票面值个数限制,请你推算出在合理的限制下信封可达到的最大邮票面值。
2.分析
2.1思路
dfs+dp
的题
2.2主要步骤
设dp[i]表示到达i需要最少邮票的数目
分析:
(1)最多能够贴N张邮票
(2)有k种面值
(3)票值1肯定是必须存在的。
(4)基本数据结构:使用一个数组cho[]用于存储选择的票值。用dp[i]表示到达价值i的最小值
针对上面的分析,就可以用dfs深搜穷举每次dp得到的票值上限—“dp过程中可以到达的最大值”,其下限则是“比当前选择的数大1的数”。
然后按照上述过程找出最优解即可。
3.代码
#include<iostream>
using namespace std;
const int N = 10000;
const int INF = 0x3f3f3f;
int n,k;//允许贴的邮票数;面值数
int cho[N],dp[N];//cho[i]表示需要使用到的数组;dp[i]表示到达i的最小邮票使用数量
int res = 0,cnt = 1;//最后的结果; 已经选了多少个数,初始为1
int ans[N];//结果数组
void dfs(int low,int up){//下限,上限
if(cnt == k){//如果已经选满了k个
if(res < up-1){
res = up -1;
for(int i = 0;i< cnt;i++){//复制整个数组
ans[i] = cho[i];
}
}
return ;
}
for(int i = low+1; i<=up;i++){
cho[cnt] = i;
cnt++;
//dp计算值
fill(dp,dp+N,INF);//重置
dp[0] = 0;
for(int l = 0;l < cnt;l++){//放前l个邮票
for(int j = cho[l] ; j <= n*cho[cnt-1]; j++){//
if(dp[j-cho[l]] != INF){//如果l-cho[j]可达
dp[j] = min(dp[j],dp[j-cho[l]]+1);
}
}
}
int temp;
//找出dp到达的最大值
for(int j = 0;j<=n*cho[cnt-1]+1;j++){
if(dp[j] > n){
temp = j;
break;//找出上限值
}
}
dfs(i+1,temp);
cnt--;//清零
}
}
int main(){
cin >> n >> k;
//初始化
cho[0] = 1;
fill(dp,dp+N,INF);
dp[0] = 0;
dfs(1,n+1);//[1,n]
for(int i = 0;i< k;i++){
cout << ans[i]<<" ";
}
cout <<"\nMAX="<< res <<"\n";
}