poj 1014 Dividing

Dividing
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 59376 Accepted: 15337

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
Marsha和Bill收集了一堆弹珠,他们想分了这堆弹珠,而且每人能分到的一样多。如果这些弹珠都长得一样就没这麻烦题目了,直接对半分就完事了。不过倒霉的是,有些弹珠大,还漂亮。因此,Marsha和Bill决定给这些弹珠划分等级,用1~6的自然数。现在,他们准备分了!结果又悲剧了,他们突然意识到这样分不是个事(哪怕这个总价值是个偶数)。比如,如果那里有一个弹珠价值1,另一个价值3,两个价值4,他们就没办法平分了。因此,他们让你写个程序去检查是这些弹珠能否均分。

Input

Each line in the input file describes one collection of marbles to be divided. The lines contain six non-negative integers n1 , . . . , 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.
每一行输入用来表示一堆要被瓜分的弹珠,包括留个正整数n1~n6,ni表示价值为i的弹珠有n个,例如,上面描述的输入应为1 0 1 2 0 0。弹珠总价值不超过20000.

Output

For each collection, 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.
对于每一个输入,输出 "Collection #k:",k表示第几个test case,每个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.

Source


这道题那个超强的剪枝加上去,真的是爆搜都能过。。。虽然还是没懂那个剪枝是怎么回事。。。
那个剪枝就是如果某个价值的弹珠超过了60个,如果是偶数,就直接给60,如果是奇数,就给61。。。
正常的做法就是一个多重背包。。。然后这个背包要求是正好装满一半的总价值,所以要给dp[0]= 0;其他给负无穷,最判断dp[sum]是不是0就行。。。
也可以把自身的价值,当做w给加上去,最后判断dp[sum] ==sum,这样做也是一样的,不过还一样的就是,这样做会超时。。。。
所以还要加上那个二进制分解。。。那个也很厉害。。。大数分解成二进制数列后,也能保证有所有的组合方式。。。这样就会控制在16ms
啊啊啊。但是加上那个超强的剪枝。。。不管怎么暴力。。。都是0ms。。。。。。。。

#include <stdio.h>
#include <string.h>

int max_dp(int a, int b);

struct div{
    int value;
    int amount;
};

struct div divide[111];
int main(){

    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);

    int sum = 1, case_num = 1;
    int mar_val[7];
    int dp[66666];

    for (int i = 0;i < 6;i ++){
        scanf("%d", &mar_val[i]);
    }

    //while(~((mar_val[0] == 0) && (mar_val[1] == 0) && (mar_val[2] == 0) && (mar_val[3] == 0) && (mar_val[4] == 0) && (mar_val[5] == 0) )){
    while(1){
        sum = 0;

        for (int i = 0;i < 6;i ++){
            if (mar_val[i] > 60)
                if (mar_val[i] % 2)
                    mar_val[i]= 61;
                else mar_val[i] = 60;

            sum += (i +1)* mar_val[i];
        }
        if (sum % 2 != 0){
            printf("Collection #%d:\n", case_num);
            printf("Can't be divided.\n\n");
            case_num++;
            for (int i = 0;i < 6;i ++){
                scanf("%d", &mar_val[i]);
            }
            continue;
        }

        sum /= 2;
        int count_n = 0;
        for (int i = 1;i < 7;i ++){
            int m = mar_val[i-1];
            int two_c = 1;

            while(two_c <= m){
                divide[count_n].value = i;
                divide[count_n++].amount = two_c;
                m-=two_c;
                two_c *= 2;

            }
            divide[count_n].value = i;
            divide[count_n++].amount = two_c;
        }
        //printf("sum = %d\n", sum);

        if (sum == 0)
            break;

        memset(dp, 0, sizeof(dp));
        for (int i = 1;i < 66666;i ++)
            dp[i] = -66666;

        for (int i = 1;i < 7;i ++){
            for (int j = 0;j < mar_val[i - 1];j ++)
                for (int k = sum;k >= i;k --)
                    if (k >= i)
                        //dp[k] = max_dp(dp[k], dp[k - i] + i);
                        dp[k] = max_dp(dp[k], dp[k - i]);
        }
        /*for (int i = 0;i < count_n;i ++){
            int tmp = divide[i].value * divide[i].amount;
            for (int j = sum; j >= tmp;j --){
                dp[j] = max_dp(dp[j], dp[j-tmp]+tmp);
            }
        }*/
        printf("Collection #%d:\n", case_num);
        //printf("dp = %d\n", dp[sum]);
        //if (dp[sum] == sum)
        if (dp[sum] == 0)
            printf("Can be divided. \n\n");
        else
            printf("Can't be divided.\n\n");

        case_num++;

        for (int i = 0;i < 6;i ++){
            scanf("%d", &mar_val[i]);
        }

        //printf("Can be divided. \n");
        //for(int i = 0;i < 6;i ++){
        //    printf("%d", mar_val[i]);
        //}

    }
    return 0;
}

int max_dp(int a, int b){
    return a > b ? a : b;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值