[POI2008]POD-Subdivision of Kingdom(搜索+状压)
题意
给定一个n个点的无向图,要求将点集分成大小相等的两个子集,使两个子集之间的边数最少
(n<=26)
题解
一开始想了半天DP发现不会,去看题解全是搜索。
所以发现C(1326)可以过我就写搜索了。
这个搜索重点在于如何快速求出两个集合的交集有多少元素。
可以预处理规模为n/2的集合的元素数记为cnt[s]
然后求当前集合的元素个数就可以这么求:cnt[x>>(n/2)]+cnt[x-((x>>n/2)<<n/2)](x为所求集合)
然后就过了
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int N=50; 8 int n,m,s[N],a[N],ans,book,anss,cnt[20000]; 9 int work(int x){ 10 return cnt[x>>(n/2)]+cnt[x-((x>>n/2)<<n/2)]; 11 } 12 int js(int x){ 13 int tmp=0; 14 while(x){ 15 if(x&1)tmp++; 16 x>>=1; 17 } 18 return tmp; 19 } 20 void dfs(int num,int now,int S,int sum){ 21 if(num==n/2+1){ 22 if(sum<ans){ 23 anss=book; 24 ans=sum; 25 } 26 return; 27 } 28 for(int i=now;i<=n;i++){ 29 if(book&(1<<i-1))continue; 30 book|=1<<i-1; 31 int b=work(S&s[i]); 32 int hh; 33 if(num==1)hh=work(s[i]); 34 else hh=sum-b+a[i]-b; 35 dfs(num+1,i+1,S|(1<<i-1),hh); 36 book^=1<<i-1; 37 } 38 } 39 int main(){ 40 scanf("%d%d",&n,&m); 41 for(int i=1;i<=m;i++){ 42 int u,v; 43 scanf("%d%d",&u,&v); 44 s[u]|=1<<v-1; 45 s[v]|=1<<u-1; 46 a[u]++;a[v]++; 47 } 48 for(int i=0;i<=(1<<n/2)-1;i++){ 49 cnt[i]=js(i); 50 } 51 ans=99999999; 52 dfs(1,1,0,0); 53 for(int i=1;i<=n;i++){ 54 if(anss&(1<<i-1))printf("%d ",i); 55 } 56 return 0; 57 }