USCAO 2.2.2

来源:互联网 发布:js获取文件大小 编辑:程序博客网 时间:2024/06/10 06:30

      首先这个题首先有一个优化就是:n个数的总和为sum:=n*(n+1)div 2,当且仅当sum为偶数的时候才有解,sum为奇数时直接输出0并且退出程序;但是这样做了也还是会超时。

      那么仔细分析一下这个题是一个0/1背包的问题,将一个集合划分成 两个“元素总和相等”的集合,设原集合的元素总和为sum,则划分后的集合的元素综合都为sum/2。那么我们可以把sum/2看成背包的容量,原集合中的数字看为物品的重量及价值(这里价值维度可以淡化,就像装箱问题)。则原问题转化为 “从原集合中选出n个物品,使这n个物品恰好放满容量为sum/2的背包的方案总数”。

   设分成的子集为set1,set2,sum=(n*(n+1)div 2)div 2。设num[i,j]表示取前i个数,使set1总数和为j的方案数.第i个数的值为i,根据是否取第i个数就有:

           num[i,j]=num[i-1,j]+num[i-1,j-i]    j-i>=0 
           num[i,j]=num[i-1,j]                      j-i<0

    则最后的结果为num[n,sum] div 2。

【代码】:

 

//dp[i][j]表示  前 I个和为 J的可能有几种
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int n,sum;
long long num[40][780];
int main()
{
 freopen("subset.in","r",stdin);
 freopen("subset.out","w",stdout);
 
 cin>>n;
 if((n*(n+1)/2)%2)
 {
  cout<<"0"<<endl;
  return 0;
 }
 sum=n*(n+1)/4;
 memset(num,0,sizeof(num));
 num[1][1]=1;
 num[1][0]=1;
 for(int i=2;i<=n;i++)
 {
  for(long j=0;j<=i*(i+1)/2;j++)
   if(j>=i)
    num[i][j]=num[i-1][j]+num[i-1][j-i];
   else
    num[i][j]=num[i-1][j];
 }
 cout<<num[n][sum]/2<<endl;
 return 0;

}

原创粉丝点击