POJ 1014 Dividing

题目大意:有六种等级的石头,已知每个等级石头的个数,求是否存在一种方案,使这些石头能分成两堆,满足这两堆石头的等级之和相等。

思路:一开始想要进行DFS,不过题目给出石头总数最大会达到20000,所以DFS显然会超时。可以对问题稍微转化一下,首先求出石头的价值总和,如果是奇数显然没有方案。如果是偶数则除以2,记结果为capacity,然后对等级为i的石头视为体积为i,价值为i的物品,并尽量填满capacity;令dp[i]表示体积为i的容器所装载的最大价值,如果dp[v]==v则说明存在方案,否则不存在。

    这样就把问题转化为多重背包问题。多重背包问题存在两种情况:

(1)当n[i]*i>=capacity时,则视为完全背包问题,伪代码为:

 

for j = i to capacity
    dp[j] = max (dp[j], dp[j-i]+i)


(2)当n[i]*i<capacity时,将石头分解,视为01背包问题,伪代码为:

integer:k=1,amount=n[i];
while k<amount
begin
    for j = capacity to i*k
        dp[j] = max (dp[j], dp[j-k*i]+k*i)
    amount = amount - k;
    k = k * 2;
end;
for j = capacity to i*amount
    dp[j] = max(dp[j], dp[j-amount*i]+amount*i);


 

代码:

#include <iostream>

using namespace std;

int n[7],sum_value,capacity,amount;
int dp[120005];

inline int max2(int a,int b)
{
    if (a>b)
        return a;
    return b;
}

int main()
{
    int i,j,k,test_cases=1;
    while (scanf("%d%d%d%d%d%d",&n[1],&n[2],&n[3],&n[4],&n[5],&n[6])==6)
    {
        for (i=1,sum_value=0;i<=6;i++)
            sum_value+=n[i]*i;
        if (sum_value==0)
            break;
        if (sum_value%2!=0)
        {
            printf("Collection #%d:\n",test_cases++);
            printf("Can't be divided.\n\n");
            continue;
        }
        capacity=sum_value/2;
        memset(dp,0,sizeof(dp));
        for (i=1;i<=6;i++)
        {
            if (n[i]*i>=capacity)
                for (j=i;j<=capacity;j++)
                    dp[j]=max2(dp[j],dp[j-i]+i);
            else
            {
                k=1;
                amount=n[i];
                while (k<amount)
                {
                    for (j=capacity;j>=i*k;j--)
                        dp[j]=max2(dp[j],dp[j-k*i]+k*i);
                    amount-=k;
                    k*=2;
                }
                for (j=capacity;j>=i*amount;j--)
                    dp[j]=max2(dp[j],dp[j-amount*i]+amount*i);
            }
        }
        printf("Collection #%d:\n",test_cases++);
        if (dp[capacity]==capacity)
            printf("Can be divided.\n\n");
        else
            printf("Can't be divided.\n\n");
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值