Combinatorics——HDUOJ 2110 - Crisis of HDU(母函数 / 多重背包思想)

原题


  • Problem Description

话说上回讲到HDU大战东洋小苟,结果自然是中方大胜,这一战也使得海东集团在全球同行业中的地位更加巩固。随着集团的发展,很多创业时期的元老逐步功成身退,先是8600移民海外,然后是linle夫妇退隐山林,逐渐的,最初众多的元老只剩下XHD夫妇和Wiskey三人了。
到了2020年,因为扩张过度加上老鼠数量逐年减少,公司的发展遇到了前所未有的危机,此时集团已经没有任何流动资金,更可怕的是,这个时候,wiskey也决定退出了!
退出本身并不麻烦,麻烦的是,退出的人需要取走相应比例(1/3)金额的资产。
假设公司此时一共有n种价值的资产,每种价值的资产数量已知,请帮助心烦意乱的XHD夫妇计算一共有多少种分割资产的方法。

  • Input

    输入包含多个测试实例,每个实例的第一行是一个整数n(n < 100),表示一共有n种价值的资产,接着的n行每行包含两个整数pi和mi(0 < pi,mi <10),分别表示某种价值和对应的数量,n为0的时候结束输入。

  • Output

    对于每个测试实例,请输出分割资产的方案数%10000,如果不能分割,请输出“sorry”,每个实例的输出占一行。

  • Sample Input

    2
    1 1
    2 1
    0

  • Sample Output

    1

  • 解题思路:

    此题需要注意的是:
    • 考虑总价值能不能分割成三份
    • 能分割出1/3的资产,方案数是多少!!即这1/3的资产可以由多少个的多少种价值的物体构成

    代码:

    母函数:
    #include<stdio.h>
    #include<string.h>
    int c1[10010], c2[10010];
    int main()
    {
        int i, j, k, n;
        while (scanf("%d", &n) && n)
        {
            int a[100], b[100], sum = 0;
            memset(c1, 0, sizeof(c1));
            memset(c2, 0, sizeof(c2));
            for (i = 1;i <= n;i++)
            {
                scanf("%d%d", &a[i], &b[i]);
                sum += a[i] * b[i];
            }
            if (sum % 3 != 0)
                printf("sorry\n");
            else
            {
                sum = sum / 3;
                for (i = 0;i <= a[1] * b[1];i += a[1])
                    c1[i] = 1;
                for (i = 2;i <= n;i++)
                {
                    for (j = 0;j <= sum;j++)
                        for (k = 0;k <= a[i] * b[i] && j + k <= sum;k += a[i])
                            c2[j + k] += c1[j];
                    for (j = 0;j <= sum;j++)
                    {
                        c1[j] = c2[j] % 10000;
                        c2[j] = 0;
                    }
                }
                if (c1[sum] == 0)
                    printf("sorry\n");
                else
                    printf("%d\n", c1[sum] % 10000);
            }
        }
        return 0;
    } 
    多重背包思想
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    int f[10010];
    int main()
    {
        int n, p, m;
        while (scanf("%d", &n), n)
        {
            memset(f, 0, sizeof(f));
            f[0] = 1;
            int sum = 0;
            for (int l = 0;l < n;l++)
            {
                scanf("%d%d", &p, &m);
                sum += p*m;
                for (int i = sum;i >= 0;i--)//看每一种物体p,在每一个背包i下最多能放多少数目j
                    for (int j = 1;j <= m;j++)
                        if (i - p*j >= 0) f[i] = (f[i - p*j] + f[i]) % 10000;
                        else break;
            }
            printf((sum % 3 || f[sum / 3] == 0) ? "sorry\n" : "%d\n", f[sum / 3] % 10000);
        }
        return 0;
    }
    错误的多重背包转01背包思想:
    //比如背包为3,有3件价值为3的物体,按照多重背包转01背包的思想,则背包有三种方案能够放物体,而明明只有一种方案(因为重复),所以不能用此种方法来解题
    //wa了好多次,自己挖坑自己跳。。。。还是能用母函数就用母函数吧
    #include<stdio.h>
    #include <algorithm>
    using namespace std;
    int V[1000];//100*10种物品的价值
    int dp[10000];//背包假设的最大容量100*10*10
    int AllValue;//总值
    void ZeroOnePack(int kind)///!!!!!这种方法是错误的!!会出现重复,不能把0个A看作是10个不同的A但是有相同的价值V,因为结果是要求方案的数目
    {
        int i, j;
        for (i = 0; i < kind; i++)//遍历每一种物体
            for (j = AllValue; j >= V[i]; j--)
                dp[j] = (dp[j] + dp[j - V[i]]) % 10000;
    }
    int main()
    {
        int N, i, v, m;
        int kind;
        while (scanf("%d", &N) && N != 0)
        {
            memset(V, 0, sizeof(V));
            memset(dp, 0, sizeof(dp));
            AllValue = 0;
            kind = 0;
            for (i = 0; i < N; i++)
            {
                scanf("%d%d", &v, &m);
                while (m--)//将多重背包看作01背包(如:10个A看作是10个不同的A但是有相同的价值V)
                {
                    V[kind++] = v;//每一种物品的价值
                    AllValue += v;//总价值
                }
            }
            if (AllValue % 3 != 0)
                printf("sorry\n");
            else {
                dp[0] = 1;
                AllValue = AllValue / 3;
                ZeroOnePack(kind);
                printf((dp[AllValue] == 0) ? "sorry\n" : "%d\n", dp[AllValue] % 10000);
            }
    
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值