【poj1014】 Dividing

http://poj.org/problem?id=1014 (题目链接)

题意

  给出有分别价值为1,2,3,4,5,6的6种物品,输入6个数字,表示相应价值的物品的数量,问一下能不能将物品分成两份,是两份的总价值相等。

solution 

  多年以前写的程序了,现在才写博客= =。这道题一看就是多重背包,所以我们用二进制把它拆分成01背包就很好做了,不知道的话就看《背包九讲》吧。。 

  首先我们把6种物品的总价值记为S,如果S%2==1,那么显然是无解的。考虑S%2==0的情况。我们把每个种物品用二进制法分解成若干个物品,用二进制分解的话,复杂度就是O(nlogn)的,而且也不用担心会有些情况考虑不到。之后背包dp,判断f[S/2]是否等于S/2即可。

代码

// poj1014
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define Pi 3.1415926535898
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

int f[1000010];
int T,a[10],num[1000010],tot;

void tt(int w,int v) {
    int i=0,x,tmp=0;
    while (1) {
        x=1<<i;
        if (tmp+x>v) break;
        tmp+=x;
        num[++tot]=x*w;
        i++;
    }
    x=v-tmp;
    if (x>0) num[++tot]=x*w;
}
int main() {
    T=0;
    while (++T) {
        memset(f,0,sizeof(f));
        int s=0;
        for (int i=1;i<=6;i++) {
            scanf("%d",&a[i]);
            s+=a[i]*i;
        }
        if (s==0) break;
        printf("Collection #%d:\n",T);
        if (s%2==1) {
            printf("Can't be divided.\n\n");
            continue;
        }
        int mid=s/2;
        tot=0;
        for (int i=1;i<=6;i++) tt(i,a[i]);
        for (int i=1;i<=tot;i++) 
            for (int j=mid;j>=num[i];j--) 
                f[j]=max(f[j],f[j-num[i]]+num[i]);
        if (f[mid]==mid) printf("Can be divided.\n\n");
        else printf("Can't be divided.\n\n");
    }
    return 0;
}

  

posted @ 2016-09-27 22:43  MashiroSky  阅读(340)  评论(0编辑  收藏  举报