[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 }

 

posted @ 2018-08-07 17:31  Xu-daxia  阅读(226)  评论(0编辑  收藏  举报