题目描述 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;
}