Luogu P1021 邮票面值设计
这题一看就是没有思路啊_(:3」∠)_
看起来像是类似货币系统那种背包,但是没有给定物品...
数据范围并不是很大,大概是O(搜索能过)
所以用dfs枚举每一种可能的邮票方案,再dp判断当前方案能表示的最大连续面值
dp:
枚举用几张邮票?显然不行。数组下标应该是面额,那么...
设b[i]为第i种邮票,f[j]为表示面额j所需的最少邮票数。
状态转移方程:f[j] = min(f[j],f[j-b[i]]+1);
然后从1开始枚举,找到第一个f[j+1]>n的,那么j即为能表示的最大面额。
dfs:
在递归的过程中,记录并不断用dp更新最大值。
当枚举邮票数量=k时,判断是否能更新答案,并记录当前的邮票方案。
剪枝和一些范围的判断很重要!
首先,枚举邮票的面额时,要保证不重复而且连续。
范围是[上一张邮票的价值+1,当前能表示的最大面额+1]。
其次,dp中f[]数组下标的上限是k*所选最后一种邮票面额。
写memset的话就会TLE(亲测)
代码如下
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #define MogeKo qwq using namespace std; const int maxn = 1e6+10; const int INF = 0x3f3f3f3f; int n,k,ans,a[maxn],b[maxn],f[maxn]; int dp(int x) { for(int i = 1; i <= b[x]*n; i++) f[i] = INF; for(int i = 1; i <= x; i++) for(int j = b[i]; j <= b[i]*n; j++) f[j] = min(f[j],f[j-b[i]]+1); for(int i = 0; i <= b[x]*n; i++) if(f[i+1] > n) return i; } void dfs(int x,int m) { if(x > k) { if(m <= ans) return; ans = m; for(int i = 1; i <= k; i++) a[i] = b[i]; return; } for(int i = b[x-1]; i <= m+1; i++) { b[x] = i; dfs(x+1,dp(x)); } } int main() { scanf("%d%d",&n,&k); dfs(1,0); for(int i = 1; i <= k; i++) printf("%d ",a[i]); printf("\nMAX=%d\n",ans); return 0; }