題目描述 Description(dp)
已知一個 N枚郵票的面值集合(如,{1分,3分})和一個上限 K ——表示信封上能夠貼 K張郵票。計算從 1到 M的最大連續可貼出的郵資。
例如,假設有 1分和 3分的郵票;你最多可以貼 5張郵票。很容易貼出 1到 5分的郵資(用 1分郵票貼就行了),接下來的郵資也不難:
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
【算法分析】【深搜TLE用dp】
簡單的暴搜時間複雜度會達到50^20,是我們難以承受的;
我們可以用遞推來做:
階段:以能夠成的面值爲每個階段。如樣例中一共有13個階段;
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
#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;
}
例如,N=3,K=2,如果面值分別爲1分、4分,則在1分~6分之間的每一個郵資值都能得到(當然還有8分、9分和12分);如果面值分別爲1分、3分,則在1分~7分之間的每一個郵資值都能得到。可以驗證當N=3,K=2時,7分就是可以得到的連續的郵資最大值,所以MAX=7,面值分別爲1分、3分。
MAX=7
第1張 | 1 | ||
第2張 | 1+1-1*n+1=2——4 | ||
第3張 | 2+1--2*n+1=3——7 | 3+1--3*n+1=4——10 | 4+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;
}