郵票問題(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; 
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章