见识到dp的强大了,可是我自己总找不到状态方程,写个dfs慢的要死,到24就算出结果都要10秒+...贴个别人代码先过了...dp再学习中
转自http://blog.csdn.net/zrw93/article/details/8520896
动态规划。大概思想是从1开始,1、2、3....不断加入新的数字,记录能组成的两个集合的差值。假如我们约定两个集合的差值是大于等于0的,以避免两个集合交换一下产生的重复。那么一开始我们只有一个1,集合差值只能是1。接下来我们加入了2,当加入1集合的时候,集合差是3,加入空集的时候集合差是1(2-1)。现在我们手里的集合差是3和1。再下面我们加入3,对于集合差为3的状态,我们会有6(3+3)与0(|3-3|)这两个新值,对于集合差为1的状态,我们会有4(1+3)和2(|1-3|)这两个状态。所以我们可以总结出状态转移方程:
1-n为集合可能产生的集合差 = 1-(n-1)可能产生的集合差 +或- n
问题的关键在于如何存储。一开始我的想法是开一个二维数组,记录1-n时可能的集合差,比如1的时候只有1;2的时候是1、3;3的时候是6,0,4,2。这样的问题是数组的规模是随着n指数增长的,到要求的39就会有2**39个。
实际上我们仔细想想就能发现,真正的可能的集合差最多只有780个,因为从1加到39等于780,而集合差又必须是整数,所以最多只有780个,那么我们就能简化一下数组,统一用长度为780的数组,数组里记录的是有几个集合差为数组编号的状态。比如sub[i][j] = 10,意思就是从1到i这个集合,分成两个集合使得集合差为j的分法一共有10种。这样的话空间就会减小很多。
代码如下:
# include <fstream> using namespace std; int sub[40][1000] = {{0}}; int main() { ifstream fin("subset.in"); ofstream fout("subset.out"); int num; fin >> num; sub[1][1] = 1; //if we only have 1, the sub of two sets could only be 1 for (int i = 2; i <= num; ++i) { for (int j = 0; j < 781; ++j) { if(sub[i-1][j] == 0) continue; sub[i][j + i] += sub[i-1][j]; if(j - i > 0) sub[i][j-i] += sub[i-1][j]; else sub[i][i-j] += sub[i-1][j]; } } fout << sub[num][0] << ' '; fin.close(); fout.close(); return 0; }