【洛谷】P1021 邮票面值设计

23 篇文章 0 订阅
11 篇文章 2 订阅

【洛谷】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";	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

说文科技

看书人不妨赏个酒钱?

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

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

打赏作者

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

抵扣说明:

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

余额充值