【题解】P1021 邮票面值设计

题面

题目传送门

前言

莫名其妙卡了云落两个点……

正文

嗯,这种自己构造方案的操作很难不想到搜索

问题是爆搜肯定是没有前途滴!

如果邮票的种类没有用完,就在可能的搜索范围内进行下一步拓展

我们记当前要确定第 \(x\) 种邮票,边界条件为 \(x==k+1\)

如果没有到边界条件,那么就依据范围拓展

范围是 \([a_{x-1}+1,当前最大面值+1]\)

否则就求当前最大面值

显然我们需要维护一个操作,统计当前最大面值

这个显然可以用 DP 维护

具体地,这是一个完全背包

\(dp_i\) 表示面值组成 \([1,i]\) 最少的邮票花费数目

转移方程:\(dp_i = \mathop{min}\limits_{j = a_i}^{a_{x} \times n} \lbrace dp_{j - a_i} + 1 \rbrace\)
然后就无了……

代码

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=5e4+10,inf=2147483647;
int n,k,a[maxn],b[maxn],dp[maxn],ans;
inline int DP(int x){
	for(int i=1;i<maxn;i++){
		dp[i]=inf;
	}
	dp[0]=0;
	for(int i=1;i<=x;i++){
		for(int j=a[i];j<=a[x]*n;j++){
			if(dp[j-a[i]]<n){
				dp[j]=min(dp[j],dp[j-a[i]]+1);
			}
		}
	}
	int res=0;
	while(dp[res+1]<=100){
		res++;
	}
	return res;	
}
inline void dfs(int x){
	if(x==k+1){
		for(int i=1;i<maxn;i++){
			dp[i]=inf;
		}
		int res=DP(x-1);
		if(res>ans){
			for(int i=1;i<=k;i++){
				b[i]=a[i];
				ans=res;
			}
		}
		return;
	}
	int mx=DP(x-1);
	for(int i=a[x-1]+1;i<=mx+1;i++){
		a[x]=i;
		dfs(x+1);
		a[x]=0;
	}
	return;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>k;
	a[1]=1;
	dfs(2);
	for(int i=1;i<=k;i++){
		cout<<b[i]<<' ';
	}
	cout<<endl<<"MAX="<<ans<<endl;
	return 0;
}

后记

nyn 学姐好巨啊

完结撒花!

posted @ 2024-12-25 17:32  sunxuhetai  阅读(76)  评论(0)    收藏  举报