1. 程式人生 > >USACO 2.2 Subset Sums 【經典的方案DP+必要的轉化】

USACO 2.2 Subset Sums 【經典的方案DP+必要的轉化】

string clu 兩個 class first namespace cin man pen

題目在這裏:https://www.luogu.org/problem/show?pid=1466#sub

1、把一個連續正整數集合平分,假設該正整數集合和為s,那麽平分後的兩個集合和均為s/2,因此我們首先把s是奇數的n排除掉 2、接著,我們發現:平分集合方案數======》用n個數湊s/2的方案數======》DP 3、若用f[i][j]表示用前i 個數湊j 的方案,則 f[i][j]=f[i-1][j]+f[i-1][j-i] (1<=i<=n,0<=j<=s/2) 4、當然,二維可以壓成一維,代碼如下: 二維 -------------------------------------------------------

技術分享
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
long long f[50][5050]={0};//how many methods are there to make up j with the first i numbers

int main()
{
    freopen("subset.in","r",stdin);
    freopen("subset.out","w"
,stdout); cin>>n; if(n==0||n*(n+1)%4!=0) { cout<<"0"<<endl; return 0; } int s=n*(n+1)/2; s/=2; f[1][1]=1; f[1][0]=1; for(int i=2;i<=n;i++) for(int j=s;j>=0;j--)//分兩種情況:i能拼起j(取i或不取), 或不能(只能不取) if(j>=i) f[i][j]
=f[i-1][j-i]+f[i-1][j]; else f[i][j]=f[i-1][j]; cout<<f[n][s]/2<<endl; return 0; }
View Code 一維 ------------------------------------------------------------------ 技術分享
#include<cstdio> 
int n,s; 
long long f[400]={0}; 
int main()
{     
   scanf("%d",&n);    
   s=n*(n+1)/2;     
   if (s&1)
   {         
    printf("0");         
    return 0;    
   }    
  s/=2;    
  f[0]=1;     
  for(int i=1;i<=n;i++)         
   for(int j=s;j>=i;j--)            
    f[j]+=f[j-i];     
  printf("%d\n",(long long)f[s]/2); 
  return 0;
}
View Code

USACO 2.2 Subset Sums 【經典的方案DP+必要的轉化】