[USACO2.2]集合 Subset Sums
阿新 • • 發佈:2018-12-13
連結
大意
求 中選出若干個數,使得選的數之和等於沒被選的數之和
資料範圍:
思路
很顯然,這個和必然為 ,所以為
想到這裡你打個狀壓( )就可以愉快的 3個點了,因為會 飛,但我們可以用累死購物券的方法把搜尋範圍一分為2搜尋兩次,這樣就可以 啦!時間複雜度
還有一種方法,發現每個數都只能選一次,也就是一個01揹包,時間複雜度
狀壓程式碼
/*
ID:hzbismy1
LANG:C++
TASK:subset
*/
#define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout)
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;int n,k,sum,now,a[10001];
long long ans;
inline char Getchar()
{
static char buf[100000],*p1=buf+100000,*pend=buf+100000;
if(p1==pend)
{
p1=buf; pend=buf+fread(buf,1,100000,stdin);
if (pend==p1) return -1;
}
return *p1++;
}
inline int read()
{
char c;int d=1,f=0;
while(c=Getchar(),!isdigit(c))if(c==45)d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=Getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline void write(register long long x)
{
if(x<0)write(45),x=-x;
if(x>9)write(x/10);
putchar(x%10+48);
return;
}
signed main()
{
file(subset);
n=read();k=n>>1;
sum=((1+n)*n)>>1;if(sum&1) return puts("0")&0;
for(int i=0;i<(1<<k);i++)//暴力前半部分
{
now=0;
for(register int j=0;i>>j;j++) if((i>>j)&1) now+=j+1;
a[now]++;
}
for(int i=0;i<(1<<(n-k));i++)//暴力後半部分
{
now=0;
for(register int j=0;i>>j;j++) if((i>>j)&1) now+=j+k+1;
if(sum/2>=now) ans+=a[sum/2-now];
}
printf("%lld\n",ans/2);//算了兩次,所以要除以2
}
dp程式碼
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;int n,k,sum,now;
long long f[10001];
inline char Getchar()
{
static char buf[100000],*p1=buf+100000,*pend=buf+100000;
if(p1==pend)
{
p1=buf; pend=buf+fread(buf,1,100000,stdin);
if (pend==p1) return -1;
}
return *p1++;
}
inline int read()
{
char c;int d=1,f=0;
while(c=Getchar(),!isdigit(c))if(c==45)d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=Getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline void write(register long long x)
{
if(x<0)write(45),x=-x;
if(x>9)write(x/10);
putchar(x%10+48);
return;
}
signed main()
{
n=read();
sum=((1+n)*n)>>1;if(sum&1) return puts("0")&0;
f[0]=1;
for(register int i=1;i<=n;i++)
for(register int j=sum;j>=i;j--)
f[j]+=f[j-i];
printf("%lld",f[sum/2]/2);//因為我們算了兩次,所以要除以2
}