动态规划,多重背包,保存路径,用完全背包的方法做多重背包(Charlie's Change,poj 1787)

就是用拆分物品的方法做的多重背包啦,时间复杂度为O(V∑logN[i]),空间复杂度为O(V)。由于是拆分物品,所以保存路径时不但要记录转移前的位置,还要记录当前硬币的类型以及使用的个数,输出前沿着路径找回去统计一下个数就好啦。一直WA不知所措,以为是算法问题一直找BUG,后来发现原来是一个quarters本来是25cents抄成了15cents。最后是自己带数据去测试才找到问题所在。写了这么多题觉得自己带数据找BUG才是王道= =。无论是交之前检查还是交之后WA回来找BUG都很不错。在比赛时尽量别太着急着去交代码吧,要拿出如果交上去WA了怎么办的心态去调试。尽量先检查几遍代码,实在找不到BUG或觉得应该不会错了就果断自己带一些数据再去搞一搞。


如果用完全背包的做法去做多重背包,就需要用到一个used数组,时间复杂度为O(V),空间复杂度为O(V)。


一共就4种物品所以当做N=4为常数了,否则正常的时间复杂度为:O(VN)。


参考1:http://blog.csdn.net/libin56842/article/details/9470687 拆分物品法

参考2:http://blog.csdn.net/liuqiyao_01/article/details/8936716 完全背包法


代码1:拆分物品法

#include
#include
using namespace std;
const int maxv = 100007;
const int inf = 0x3f3f3f3f;

int pri;
int v[4];
int w[4]={1,5,10,25};
int dp[maxv];
int p[maxv];
int c[maxv];
int ans[4];

int main()
{
    while(scanf("%d",&pri)==1&&pri)
    {
        for(int i=0;i<4;i++)
            scanf("%d",&v[i]);
        memset(dp,-inf,sizeof(dp));
        dp[0]=0;
        for(int i=0;i<4;i++)
        {
            int res=v[i];
            for(int j=1;j<=res;j<<=1)
            {
                res-=j;
                int W=j*w[i];
                for(int k=pri;k>=W;k--) if(dp[k-W]+1>dp[k])
                {
                    dp[k]=dp[k-W]+j;
                    p[k]=k-W;
                    c[k]=i*maxv+j;
                }
            }
            if(res)
            {
                int W=res*w[i];
                for(int k=pri;k>=W;k--) if(dp[k-W]+1>dp[k])
                {
                    dp[k]=dp[k-W]+res;
                    p[k]=k-W;
                    c[k]=i*maxv+res;
                }
            }
        }
        if(dp[pri]<0) puts("Charlie cannot buy coffee.");
        else
        {
            memset(ans,0,sizeof(ans));
            int now=pri;
            while(now)
            {
                ans[c[now]/maxv]+=c[now]%maxv;
                now=p[now];
            }
            printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[0],ans[1],ans[2],ans[3]);
        }
    }
    return 0;
}

代码2:完全背包法

#include
#include
#define maxv 100010
#define inf 0x3f3f3f3f
using namespace std;

int v[4]={1,5,10,25};
int n[4];
int V;
int dp[maxv];
int p[maxv];
int used[maxv];
int ans[50];

int main()
{
    while(scanf("%d",&V)==1&&V)
    {
        for(int i=0;i<4;i++)
            scanf("%d",&n[i]);
        for(int i=0;i<=V;i++)
            dp[i]=-inf;
        dp[0]=0;
        for(int i=0;i<4;i++)
        {
            memset(used,0,sizeof(used));
            for(int j=v[i];j<=V;j++) if(dp[j-v[i]]>=0&&dp[j-v[i]]+1>dp[j]&&used[j-v[i]]


你可能感兴趣的:(动态规划,背包问题,保存路径)