洛谷 P2725 解題報告
阿新 • • 發佈:2018-05-07
tdi int 計算 main 枚舉 str 接下來 true mem
P2725 郵票 Stamps
題目背景
給一組 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。 [規模最大的一個點的時限是3s]
小提示:因為14貼不出來,所以最高上限是13而不是15
輸入輸出格式
輸入格式:
第 1 行: 兩個整數,K 和 N。K(1 <= K <= 200)是可用的郵票總數。N(1 <= N <= 50)是郵票面值的數量。
第 2 行 .. 文件末: N 個整數,每行 15 個,列出所有的 N 個郵票的面值,每張郵票的面值不超過 10000。
輸出格式:
第 1 行:一個整數,從 1 分開始連續的可用集合中不多於 K 張郵票貼出的郵資數。
這是一個我一開始就想偏了的完全背包。
一開始:
#include <cstdio>
const int N=201;
const int inf=0x3f3f3f3f;
int max(int x,int y) {return x>y?x:y;}
int min(int x,int y) {return x>y?y:x;}
bool dp[3000010];//表示在第i張時面值k是否ok
int k,n;//郵票總數,面值數
int kind[52];
int m_min=inf,m_max=0;
int main()
{
scanf("%d%d",&k,&n);
for (int i=1;i<=n;i++)
{
scanf("%d",kind+i);
m_max=max(kind[i],m_max);
m_min=min(kind[i],m_min);
}
dp[0]=true;
for(int i=0;i<k;i++)
{
int l=m_min*i,r=m_max*i;
for(int p=r;p>=l;p--)
if(dp[p])
for(int j=1;j<=n;j++)
dp[p+kind[j]]=true;
}
for(int i=1;i<=m_max*k+1;i++)
{
if(!dp[i])
{
printf("%d\n",i-1);
break;
}
}
return 0;
}
三維的呢。
完全背包:
#include <cstdio>
#include <cstring>
const int N=201;
const int inf=0x3f3f3f3f;
int max(int x,int y) {return x>y?x:y;}
int min(int x,int y) {return x>y?y:x;}
int dp[3000010];//表示在組成面值為i時用的最小郵票數
int k,n,m_max=0;//郵票總數,面值數
int kind[52];
int main()
{
memset(dp,0x3f,sizeof(dp));
scanf("%d%d",&k,&n);
for(int i=1;i<=n;i++)
{
scanf("%d",kind+i);
m_max=max(m_max,kind[i]);
}
dp[0]=0;
for(int i=1;i<=n;i++)
{
int r=m_max*k+1;
for(int j=0;j<=r;j++)
dp[j+kind[i]]=min(dp[j+kind[i]],dp[j]+1);
}
for(int i=0;;i++)
if(dp[i]>k)
{
printf("%d\n",i-1);
break;
}
return 0;
}
其實把\(k\)放在數組裏面最後比,我還真沒想到。
我所能理解的思維導向是從完全背包出發的。
每種郵票都有無限多張
註意常數優化,比如\(j\)的枚舉顯然並不是最優的
2018.5.3
洛谷 P2725 解題報告