天天看点

HDU——1059 Dividing(多重背包+二进制优化)

Problem Description

Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value.

Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.

Input

Each line in the input describes one collection of marbles to be divided. The lines consist of six non-negative integers n1, n2, …, n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line “1 0 1 2 0 0”. The maximum total number of marbles will be 20000.

The last line of the input file will be “0 0 0 0 0 0”; do not process this line.

Output

For each colletcion, output

Collection #k:'', where k is the number of the test case, and then either

Can be divided.” or “Can’t be divided.”.

Output a blank line after each test case.

Sample Input

1 0 1 2 0 0

1 0 0 0 1 1

0 0 0 0 0 0

Sample Output

Collection #1:

Can’t be divided.

Collection #2:

Can be divided.

解题思路:题目的大意就是给定一些弹珠,每个弹珠的价值不同,并分别用1~6之间的自然数来表示其价格。我们需要做的就是确定这些弹珠可不可以分成价值相等的两堆。

步骤:

1、首先判断弹珠的总价值是否为一个偶数,若为偶数则继续第三步,否则第二步。

2、总价值为奇数,则直接判定为不能分成价值相等的两堆。结束判断

3、总价值为偶数并不代表一定可以分为符合要求的两堆,因为每个弹珠都是一个整体,只有当能够从所有的弹珠中选出几个,而其总价值正好为所有弹珠总价值的一半时才能算是符合分堆要求。

看完题目,很容易就能想到0-1背包算法。然后就很潇洒的写下了 Time Limit Exceeded 的代码。

初步代码:

#include <stdio.h>
#include <string.h>
using namespace std;

int num[];
int value[];
int total[];
int tempto[];
int knapsack(int size , int num){   //size-the size of kanpsack , num-the number of things

    int i , j;
    int temp = size < value[num - ] ? size : value[num - ];
    for( i = temp ; i <= size ; i ++)
        total[i] = value[num - ];

    for( j = num -  ; j >=  ; j --){
        for( i =  ; i <= size ; i++)
            tempto[i] = total[i];
        for( i = value[j] ; i <= size ; i ++){
            if(tempto[i - value[j]] + value[j] > tempto[i])
                total[i] = tempto[i - value[j]] + value[j];
        }    
    }

    return total[size];
}
int main(){
    int i, j ,tempvalue , temptotal =  ,flag = ,caseId =  , targetnum =  ,result = ;

    while(flag == ){
        memset(num ,  , sizeof(num));
        memset(total ,  , sizeof(total));
        memset(value ,  , sizeof(value));

        tempvalue =  ;
        temptotal = ;
        for(i =  ; i <  ; i ++){
            scanf("%d",&num[i]);
            for(j = temptotal ; j < temptotal+num[i]; j ++){
                value[j] = i + ;
            }
            temptotal = temptotal + num[i];        
            tempvalue += num[i]*(i+);
        }
        if(tempvalue == ){
            flag = ;
        }else{
            caseId ++;
            printf("Collection #%d:\n",caseId);

            if(tempvalue %  != ){  //case1: the total value is odd
                printf("Can't be divided.\n\n");
            }else{  //the total value is even
                targetnum = tempvalue / ;
                //knapsack algorithm
                result = knapsack(targetnum , temptotal);
                if(result == targetnum)
                    printf("Can be divided.\n\n");
                else
                    printf("Can't be divided.\n\n");
            }
        }

    }
    return ;
}
           

本题的数据量是很大的,所以直接使用0-1背包算法肯定会超时,所以要进行进一步的优化。

本题实际上应该是一个多重背包的为题,同时采用的优化方法是二进制优化。

因为代码带很少,我也是第一次接触多重背包问题,算法和0-1背包基本一样,只是在进行0-1背包算法之前首先进行了二进制优化,这个优化方法是真真切切的第一次接触了。

二进制优化代码模板,详细链接:http://blog.csdn.net/aaaaacmer/article/details/48543789

优化后代码:

#include <stdio.h>
#include <string.h>
using namespace std;

int num[];
int dp[];
int targetnum = ;

int max(int a , int b){
    if(a > b)
        return a;
    else
        return b;
}
void zero(int cost)  
{  
    for(int i=targetnum;i>=cost;i--)  
    dp[i]=max(dp[i],dp[i-cost]+cost);  
}  
void complet(int cost)  
{  
    for(int i=cost;i<=targetnum;i++)  
    dp[i]=max(dp[i],dp[i-cost]+cost);  
}  
void multi(int cost ,int amount)  
{  
    if(cost*amount>=targetnum)  
    {  
        complet(cost);  
        return;  
       }  
       int k=;  
       while(k<amount)  
       {  
        zero(k*cost);  
        amount-=k;  
        k=k*;  
    }  
    zero(amount*cost);  
} 

int main(){
    int i, j ,tempvalue ,flag = ,caseId =  ,result =  , k =  ;

    while(flag == ){
        memset(num ,  , sizeof(num));
        memset(dp ,  , sizeof(dp));
        tempvalue =  ;
        for(i =  ; i <  ; i ++){
            scanf("%d",&num[i]);        
            tempvalue += num[i]*(i+);
        }
        if(tempvalue == ){
            flag = ;
        }else{
            caseId ++;
            printf("Collection #%d:\n",caseId);
            if(tempvalue %  != ){  //case1: the total value is odd
                printf("Can't be divided.\n\n");
            }else{  //the total value is even
                targetnum = tempvalue / ;
                for(i =  ; i <  ; i ++)
                    multi(i+ , num[i]);
                if(dp[targetnum] == targetnum)
                    printf("Can be divided.\n\n");
                else
                    printf("Can't be divided.\n\n");
            }
        }   
    }
    return ;
}