原题:
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); } } }