NKOJ P1824 [USACO3.1.6] 邮票【背包DP】

显然这是一道背包 D P 。但是这道题不是朴素的 01 与无穷,因为题目规定了对于 N 种物品一共最多用 K 次,所以我们是无法通过一般的背包模型来完成这道题的。

但是这道题始终有背包 D P 的感觉,那么我们不妨就从背包的角度来想想这道题。

由于朴素的背包定义的为:

f [ i ] 表示能否填充使得空间(价值)恰好为 i ,对应的转移方程:

f [ i ] = f [ i v [ j ] ]

对于这道题,我们只需要稍微变换一下思路就可以求解了。

f [ i ] 表示填充使得价值为I时所使用的物品数量,这样来想思路其实就一下子畅通了,而相对应的转移方程也就很容易写出来:

f [ i ] = m i n { f [ i v [ j ] ] } + 1

初始化就是 f [ i ] = i n f , i 0

对于每一个空间 i ,我们在状态转移结束后判断 f [ i ] 是否小于等于 K ,不然就表明填充到空间 I 的时候,所需要的个数就大于 K 了,那么 i 1 就是我们需要的答案。

#include <bits/stdc++.h>
#define For(I,L,R) for(I=(L);I<=(R);I++)
using namespace std;

inline int Read(){
    int X=0;char CH=getchar();bool F=0;
    while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
    while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
    return F?-X:X;
}

const int Max=2e6+5;
const int Inf=1e9;
int N,M,V[Max],DP[Max];

int main(){
    int I,J,K;

    N=Read(),M=Read();

    For(I,1,M) V[I]=Read();

    For(I,1,2000000) {
        DP[I]=Inf;

        For(J,1,M) if(I>=V[J])
            DP[I]=min(DP[I],DP[I-V[J]]+1);

        if(DP[I]>N){
            printf("%d\n",I-1);return 0;
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/81636465