JRM
For many sets of consecutive integers from 1 through N (1 <= N <= 39),one can partition the set into two sets whose sums are identical.
For example, if N=3, one can partition the set {1, 2, 3} in one way sothat the sums of both subsets are identical:
- {3} and {1,2}
This counts as a single partitioning (i.e., reversing the order countsas the same partitioning and thus does not increase the count ofpartitions).
If N=7, there are four ways to partition the set {1, 2, 3, ... 7} so thateach partition has the same sum:
- {1,6,7} and {2,3,4,5}
- {2,5,7} and {1,3,4,6}
- {3,4,7} and {1,2,5,6}
- {1,2,4,7} and {3,5,6}
Given N, your program should print the number of ways a set containingthe integers from 1 through N can be partitioned into two sets whosesums are identical. Print 0 if there are no such ways.
Your program must calculate the answer, not look it up from a table.
PROGRAM NAME: subset
INPUT FORMAT
The input file contains a single line with a single integer representingN, as above.SAMPLE INPUT (file subset.in)
7
OUTPUT FORMAT
The output file contains a single line with a single integer that tellshow many same-sum partitions can be made from the set {1, 2, ..., N}.The output file should contain 0 if there are no ways to make asame-sum partition.
SAMPLE OUTPUT (file subset.out)
4
————————————————————吃饱的分割线————————————————————
前言:一开始用暴力枚举,二进制法生成子集。
for(int s = 0; s < (1<<n); s++) {
for(int i = 0; i < n; i++) {
if(s&(1<<i))
printf("%d ", num[i]);
}
}
可是超时。减掉一半计算过程(剩下枚举的一半恰好是前一半的补集)还是超时。然后就不得不用动态规划来做了。
思路:仔细思考,发现其实对于每个数字,集合中只能取一次,而且当且仅当集合的和等于原来集合的总和的一半的时候产生可行方案。
这就是01背包问题啦。每个数字就是本身的体积,求的是恰好装满背包的可行方案数。
f[i][j]值为可行方案数,i为前i个物品,j为现有体积。
那么f[n-1][S/2] / 2即为答案(方案有一半是重复的)。
状态转移:由于不考虑价值,所以每一步都可以转移:
f[i][j] = f[i-1][j] + f[i-1][j-i];
意即:前i个物品构成j的可行方案 = 前i-1个物品构成j的方案加上前i-1个物品gpu成j-i的方案(因为可以加上i的体积恰好构成j)
PS:除此之外,因为S = (n+1)*n/2,V = S / 2,所以(n+1)*n如果不能整除4,就直接输出0
代码如下:
/*
ID: j.sure.1
PROG: subset
LANG: C++
*/
/****************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <string>
#include <iostream>
using namespace std;
/****************************************/
int n;
long long dp[800];
int main()
{
freopen("subset.in", "r", stdin);
freopen("subset.out", "w", stdout);
scanf("%d", &n);
if(n*(n+1) % 4)
printf("0\n");
else {
///背包问题
int V = n*(n+1)/4;
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = V; j >= i; j--) {
dp[j] = dp[j] + dp[j-i];
}
}
printf("%lld\n", dp[V]/2);
}
return 0;
}