邮票问题(DFS)

题目描述 Description(dp)

已知一个 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 NK1 <= K <= 200)是可用的邮票总数。N1 <= N <= 50)是邮票面值的数量。

 2 ..文件末: N 个整数,每行 15个,列出所有的 N个邮票的面值,每张邮票的面值不超过 10000

输出描述 OutputDescription

 1行:一个整数,从 1分开始连续的可用集合中不多于 K张邮票贴出的邮资数。

样例输入 Sample Input

5 2

1 3

样例输出 Sample Output

13

算法分析】【深搜TLE用dp】

简单的暴搜时间复杂度会达到50^20,是我们难以承受的;

我们可以用递推来做:

阶段:以能够成的面值为每个阶段。如样例中一共有13个阶段;

状态:f[i]表示构成面值i所需的最少邮票个数;

f[0]=0 ;  f[1]=1   f[2]=2   f[3]=1  
f[4]=2    f[5]=3   f[6]=2   f[7]=3
f[8]=4    f[9]=3   f[10]=4  f[11]=5
f[12]=4   f[13]=5

决策:f[i]=min(f[i],f[i-a[j]]+1);           
           f[i]=min(m, f[i-a[j]] +1)

#include<stdio.h>
int min(int a,int b)
{
	if(a<b)
	 return a;
	else 
	 return b; 
 }
 
 int main()
 {
 	int n,k;
 	int f[1000];
 	printf("请输入邮票张数(n)和邮票面值种类(k):");
 	scanf("%d %d",&n,&k);
 	int a[n];
 	printf("请输入邮票的面值:");
 	for(int i=0;i<k;i++)
 	{
 		scanf("%d",&a[i]);
	 }
 	f[0]=0;
 	int j=0;
 	while(f[j]<=n)     //邮票的张数不能超过 n 
 	{
 		j++;
 		int m=4000000;
 		for(int i=0;i<k;i++)
 		{
		 if(j>=a[i])         // 邮票构成的面值要  >=a[i]  这样f[j-a[i]]才够减
		 {
		 	m=min(m,f[j-a[i]]+1);
 		    f[j]=m;
		  } 
		 
 		 
	 }
   }
   printf("%d",j-1);
	 return 0;
}


问题描述 (深搜+dp)
  给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤13)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值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分。
输入格式
  一行,两个数N、K
输出格式
  两行,第一行升序输出设计的邮票面值,第二行输出“MAX=xx”(不含引号),其中xx为所求的能得到的连续邮资最大值。
样例输入
3 2
样例输出
1 3
MAX=7
 问题分析: 

         一定有面值为1 的邮票,否则  max=0;  那么第二张邮票的面值  2--1*n+1; (n:贴n张邮票); 如果已经选定了dep-1个面值,现在要选第dep个面值,那么第dep个面值的范围只能是a[dep-1]+1到a[dep-1]*n+1,这也就规定了枚举的范围,那么用深搜就好做很多了
以n=3,k=3为例:第一个面值肯定为1,但是第二个面值只能是2,3,4,因为面值为1的最多贴3张,贴满的最大值为3,要保证数字连续,那么第二个数字最大只能是4。所以我们可以得到规律,如果邮票张数为n,种类为k,那么从小到大的顺序,邮票a[i]的下一种面值的取值范围必然是a[i]+1到a[i]*n+1如图:

n=3,k=3
第1张1  
第2张1+1-1*n+1=2——4  
第3张2+1--2*n+1=3——73+1--3*n+1=4——104+1--4*n+1=5——13
#include<stdio.h>
int n,k;
	int i,j;
int max=0;
int f[1000],jl[1000],a[1000]; 
int min(int a,int b)
{
	if(a<b)
	 return a;
	else 
	return b;
}
int dp()
{

	f[0]=0;
	j=0;
	while(f[j]<=n)
	{
		j++;
		int m=4000;
		for(i=1;i<=k;i++)
		{
			if(j>=a[i])
			{
				m=min(m,f[j-a[i]]+1);
				f[j]=m; 
			}
		}
	}
	if(max<j-1)
	{
		max=j-1;
		for(i=1;i<=k;i++)
		jl[i]=a[i];
	}	
}
void DFS(int t)
{
	if(t>k)    // 邮票面值种类  t  超过规定的 k 种邮票面值
	 dp();
	else
		for(int i=a[t-1]+1;i<=a[t-1]*n+1;i++)
		{
			a[t]=i;             //第 t张邮票的面值 
			DFS(t+1); 
		}
	  
 } 
int main()
{
	printf("请输入邮票的张数(n)和邮票的种类(k):");
	scanf("%d %d",&n,&k);
	a[1]=1;      //第 1 张邮票的面值为1 
	DFS(2);     // 进行深搜选定第二张邮票的面值 
	for(int i=1;i<=k;i++)   // i必须从1 开始 ,因为 a[1]=1   是从1 开始的 
	printf("%d ",jl[i]);
	printf("\nMAX=%d",max);
	return 0; 
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值