【usaco/codevs2033/codevs1047/NOIP1999TG】 邮票问题浅谈
来源:互联网 发布:电驴下载软件 编辑:程序博客网 时间:2024/04/30 09:55
1、邮票 usaco/codevs2033黄金Gold
题目描述 Description
已知一个 N枚邮票的面值集合(如,{1分,3分})和一个上限 K ——表示信封上能够贴 K张邮票。计算从 1到 M的最大连续可贴出的邮资。
例如,假设有 1分和 3分的邮票;你最多可以贴 5张邮票。很容易贴出 1到 5分的邮资(用 1分邮票贴就行了),接下来的邮资也不难:
6 = 3 + 3
7 = 3 + 3 + 1
8 = 3 + 3 + 1 + 1
9 = 3 + 3 + 3
10 = 3 + 3 + 3 + 1
11 = 3 + 3 + 3 + 1 + 1
12 = 3 + 3 + 3 + 3
13 = 3 + 3 + 3 + 3 + 1
然而,使用 5枚 1分或者 3 分的邮票根本不可能贴出 14分的邮资。因此,对于这两种邮票的集合和上限 K=5,答案是 M=13。
小提示:因为14贴不出来,所以最高上限是13而不是15
输入描述 InputDescription
第 1行:两个整数,K和 N。K(1 <= K <= 200)是可用的邮票总数。N(1 <= N <= 50)是邮票面值的数量。
第 2行 ..文件末: N 个整数,每行 15个,列出所有的 N个邮票的面值,每张邮票的面值不超过 10000。
输出描述 OutputDescription
第 1行:一个整数,从 1分开始连续的可用集合中不多于 K张邮票贴出的邮资数。
样例输入 Sample Input
5 2
1 3
样例输出 Sample Output
13
数据范围及提示 Data Size& Hint
上
【数据分析】【空间复杂度可以承受】
最多贴k<=200张,每张面值不超过10^4,所以2*10^6可以用一个一维数组存储。
【算法分析】【深搜TLE用dp】
简单的暴搜时间复杂度会达到50^20,是我们难以承受的;
我们可以用递推来做:
阶段:以能够成的面值为每个阶段。如样例中一共有13个阶段;
状态:f[i]表示构成面值i所需的最少邮票个数;
决策:如果当前能组成的面值i减去某一张邮票的面值再+1<f[i]的话,就说明构成面值i的邮票个数还可以更少,就是说用当前的那一张邮票来组成面值i的话,是当前的最优解,,用状态转移方程来表示:f[i]=min(f[i],f[i-a[j]]+1);
这就是dp的思路,时间复杂度O(nk);
【代码】
<span style="font-size:18px;">#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int n,k,i,j,a[100000],f[100000];int main(){ scanf("%d%d",&n,&k); for(i=1;i<=n;++i) scanf("%d",&a[i]); sort(a+1,a+n+1); f[0]=0; i=0; while(f[i]<=k) { i++; f[i]=2147483647; for(j=1;j<=n;++j) if (a[j]<=i&&f[i-a[j]]+1<f[i]) f[i]=f[i-a[j]]+1; } printf("%d",i-1); return0;}</span>
2、邮票面值设计 NOIP1999TG/codevs1047钻石Diamond
题目描述 Description
给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤40)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值MAX,使在1~MAX之间的每一个邮资值都能得到。
例如,N=3,K=2,如果面值分别为1分、4分,则在1分~6分之间的每一个邮资值都能得到(当然还有8分、9分和12分);如果面值分别为1分、3分,则在1分~7分之间的每一个邮资值都能得到。可以验证当N=3,K=2时,7分就是可以得到的连续的邮资最大值,所以MAX=7,面值分别为1分、3分。
输入描述 InputDescription
N和K
输出描述 OutputDescription
每种邮票的面值,连续最大能到的面值数。数据保证答案唯一。
样例输入 Sample Input
3 2
样例输出 Sample Output
1 3
MAX=7
【数据分析】【空间复杂度可以接受】
N+K<=40;4*10^6(学姐说最高可达3*10^7)
【算法分析】【深搜+dp】
N+K<=40;
与上一题的区别在于还需枚举邮票的面值,枚举的过程可以用深搜来实现;
首先明确:
第一个面值一定是1,假如说最多贴n张,要保证连续,那么第二张面值最大是1*n+1,最小是1+1;也就是说,如果已经选定了dep-1个面值,现在要选第dep个面值,那么第dep个面值的范围只能是a[dep-1]+1到a[dep-1]*n+1,这也就规定了枚举的范围,那么用深搜就好做很多了;
我们可以用深搜生成很多面值的组合,然后再用类似前一题的dp来求出最大的连续面值,即是这道题的答案。
【代码】
<span style="font-size:18px;">#include<iostream>#include<cstdio>#include<cstring>using namespace std;int f[4000005],a[50],ans[50];int n,k,maxn,i; void work()//和上面的dp过程基本相同,ans和maxn是最终的答案{ inti,j; f[0]=0; i=0; while(f[i]<=n) { i++; f[i]=2147483647; for(j=1;j<=n;++j) if (i>=a[j]&&f[i-a[j]]+1<f[i]) f[i]=f[i-a[j]]+1; } if(i-1>maxn) { maxn=i-1; for(j=1;j<=k;++j) ans[j]=a[j]; } return;} void dfs(int dep){ intr; if(dep==k+1) { work();//如果已经生成好了一种可能的组合,就进行一次dp return; } for(r=a[dep-1]+1;r<=a[dep-1]*n+1;++r)//这就是前面说的枚举的范围 { a[dep]=r;//a是记录数组 dfs(dep+1); } return;} int main(){ freopen("a5.in","r",stdin); freopen("a5.out","r",stdout); scanf("%d%d",&n,&k); maxn=0; dfs(1);//首先深搜生成所有可能的面值组合 for(i=1;i<=k;++i) printf("%d ",ans[i]); printf("\n"); printf("MAX=%d",maxn); return0;}</span>
邮票问题是经典问题,在学递推和搜索的时候都出现了。通过这道题可以更深刻的理解搜索与dp各自的原理以及他们的结合。
- 【usaco/codevs2033/codevs1047/NOIP1999TG】 邮票问题浅谈
- 邮票codevs2033
- 邮票问题浅谈
- codevs1047
- 水题笔记:codevs1047 邮票面值设计 [搜索]
- Codevs 2033 邮票 USACO
- usaco ★Stamps 邮票
- 邮票问题
- 邮票问题
- 邮票问题
- 邮票问题
- 邮票问题
- 邮票问题
- 邮票问题
- 邮票问题
- 邮票问题
- 邮票问题
- 邮票问题
- 天声人語 20150813
- 南宁代办医院诊断书
- 柳州代办医院诊断书
- leetcode 114: Flatten Binary Tree to Linked List
- 桂林代办医院诊断书
- 【usaco/codevs2033/codevs1047/NOIP1999TG】 邮票问题浅谈
- POJ 2485 Highways(最小生成树prim算法)
- 梧州代办医院诊断书
- 钦州代办医院诊断书
- 贵港代办医院诊断书
- Docker之weave工具
- 百色代办医院诊断书
- 常见验证码的弱点与验证码识别
- Javascript 遍历对象的属性