`
blackcoffee
  • 浏览: 55376 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

USACO Training Section 2.2 Subset Sums

阅读更多
英文原题  中文题译 
题目大意:
  给定N,求将集合{i|1<=i<=N}划分为两个元素和相等的子集的方案的个数。如,N=3时,{1,2}{3}共一个解。

1-N的数的和为(N+1)N/2,则如果此数为奇数,一定无解。若为偶数,则一定可构造出解。
如果直接暴力求解,粗略估计搜索空间为(n/2)!,不过可以快速剪枝。关键是从最大的数开始搜,每次增加一个未搜索的最大数,直到得到既定的和,或者超过和则剪枝。例如N=7,(7+1)*7/4=14,即每个集合的和为14。最大数7必然在某个集合中,这个数不用搜索。可从6开始,得1。5开始,得2。4开始,得3,继续搜索得2,1。3开始,就不用搜了,因为从3开始最大可得(3+1)*2/2=6<7。

/*
ID: blackco3
TASK: subset
LANG: C++
*/
#include <iostream>
using namespace std;
int n_part=0 ;
void get_subset( int cur_num , int remain ) {
	if( remain<=0 )
		n_part += (remain==0);
	else 
		for( int i=min(cur_num-1,remain); remain<=((i*(i+1))>>1) && i>=1; i-- )
			get_subset( i, remain-i );
}

int main() {	
	freopen("subset.in", "r", stdin);
	freopen("subset.out", "w", stdout);	
	int num ;
	cin >> num ;
	int part_sum = (num * (num + 1) ) >> 1 ;
	if( part_sum & 1 ){
		cout << 0 << endl ;
		return 0 ;
	}
	get_subset(num, (part_sum>>1)-num );
	cout << n_part << endl ;
	return 0;
}


这样搜索在第五个测试点(36)时超时(比NOCOW上说的第四个点31超时好,呵呵),而整个测试长度为39,故需要另想办法。打印出n=8时搜索求解的结果如下:
8 7 3
8 7 2 1
8 6 4
8 6 3 1
8 5 4 1
8 5 3 2
8 4 3 2 1
注意到其中的重复性,如yu'xyu'xiyu'xia余数为3时有两种方案3,21。这种重复方案导致整个搜索呈指数复杂度。对这种具有重复子问题的求解,自然应该想到动态规划了,于是定义:a[i][j]= 以小于等于i的数求和得到j的个数。则自然有a[i][j]= a[i-1][j-i]+a[i-1][j](表示是否选择i-1)。结合之前的分析,a[N][(N+1]*N/4]即为答案。同时我们可以在此表的基础上重构出所有的解来。

在标程中有一个用一维DP的,其实是把上述的2维DP线性化。
/*
ID: blackco3
TASK: subset
LANG: C++
*/
#include <iostream>
#include <memory.h>
using namespace std;
#define _max_ 40

int main() {	
	freopen("subset.in", "r", stdin);
	freopen("subset.out", "w", stdout);	
	int num ;
	cin >> num ;
	int part_sum = (num * (num + 1) ) >> 1 ;
	if( part_sum & 1 ){
		cout << 0 << endl ;
		return 0 ;
	}
	part_sum = part_sum>>1 ;
	int sum_num[_max_][((_max_*(_max_+1))>>2)+1] ;
	memset(sum_num, 0, sizeof(sum_num));
	sum_num[1][1]=1 ;
	for( int i=2; i<=num; i++ ){
		for(int j=1; j<=part_sum; j++ ){
			sum_num[i][j] += i-1>=1 ? sum_num[i-1][j] + ( j-i>=1 ? sum_num[i-1][j-i] : 0 ) : 0 ;
		}
	}
	cout << sum_num[num][part_sum] << endl ;
	return 0;
}


PS:求解之初曾想过直接对N做DP,查了半天数据没找到可行的合适方案。

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics