USACO-cha2-sec2.2 Subset Sums

16 篇文章 0 订阅
Subset Sums
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;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值