Subset Sums chapter 2.2

 见识到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] << '\n';  
    fin.close();  
    fout.close();  
    return 0;  
}  

 

 

 

posted @ 2013-08-28 19:59  cavehubiao  阅读(174)  评论(0编辑  收藏  举报