poj-1742Coins (多重背包的可行性问题)

题目:从价值和数量分别为a[i]和c[i]的n种硬币中,最多可以组成多少不超过m价值的组合?也就是问在1-m中,有多少个数可以由这些硬币加和得到。(楼教主的男人八题之一,算是一类经典问题)

与多重背包问题很相似,但是用多重背包直接dp或者简单优化的dp很可能会T。

(感觉自己都会写,其实不是那么一回事,并不代表就能AC)

dp[i][j]表示前i种硬币构成j的钱数时,第i种硬币还剩多少,(不能构成则为负数)。

如果dp[i-1][j]>=0(即前i-1种已经可以加和为j),则dp[i][j]=c[i](即全部剩下)。

如果上述不成立并且a[i]<j,那么肯定此时不能加和为j,dp[i][j]=-1。

其他情况直接dp[i][j-a[i]]-1即可。

因为题中i和 i-1状态不必同时出现,又因为dp[j-a[i]]是向后更新的,所以可以重复利用一维dp数组,更新时正循环。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int maxn=110;
const int maxm=100010;
int a[maxn],c[maxn],dp[maxm];
int n,m;
int main()
{
    while(~scanf("%d%d",&n,&m),n+m)
    {
        memset(dp,-1,sizeof(dp));
        dp[0]=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
            scanf("%d",&c[i]);
        for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)///j++为正循环
        {
            if(dp[j]>=0) ///判断条件中的dp[j]其实是dp[i-1][j]
                dp[j]=c[i];
            else if(j<a[i])
                dp[j]=-1;
            else
                dp[j]=dp[j-a[i]]-1;
        }
        int ans=0;
        for(int i=1;i<=m;i++)
            if(dp[i]>=0) ans++;
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dllpxfire/article/details/81138716