还可以这么搜......学到了(PoPoQQQ orz)
我们最朴素的做法是枚举所有状态(当然可以剪,剪完最终实际状态量也是$C_{26}^{13}$的),然后每次$O(n)$扫一遍判断,大概会T炸,考虑优化
我们先预处理每个状态中$1$的数目和连边的状态,然后压缩状态初始让一边集合为空,一边集合为全集,这样每次从已有的点的前面$\frac{n}{2}$个点中枚举一个加入另一边,就可以边搜边更新边数而不用最后$O(n)$检查了。另一个问题是数组可能非常大,这里我们可以把状态拆成前后两半,然后检查的时候检查两半再拼起来就好了。学了学技巧和思想还是挺好的说......
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int ss[28],cnt[1<<13]; 6 int n,m,t1,t2,ans=2e9,anss,num,half; 7 int s(int x) 8 { 9 return 1<<(x-1); 10 } 11 int getst(int x) 12 { 13 return cnt[x&half]+cnt[x>>num]; 14 } 15 void DFS(int last,int noww,int state,int numb) 16 { 17 if(noww>n) return ; 18 if(noww==num) 19 { 20 if(numb<ans) 21 ans=numb,anss=state; 22 return ; 23 } 24 for(int i=last;i<=n;i++) 25 DFS(i+1,noww+1,state|s(i),numb-getst(state&ss[i])+getst((~state)&ss[i])); 26 } 27 int main () 28 { 29 scanf("%d%d",&n,&m); 30 num=n>>1,half=(1<<num)-1; 31 for(int i=1;i<=m;i++) 32 { 33 scanf("%d%d",&t1,&t2); 34 ss[t1]|=s(t2),ss[t2]|=s(t1); 35 } 36 for(int i=1;i<=half;i++) 37 cnt[i]=cnt[i>>1]+(i&1); 38 DFS(1,0,0,0); 39 for(int i=1;i<=n;i++) 40 if(anss&s(i)) printf("%d ",i); 41 return 0; 42 }
原文地址:https://www.cnblogs.com/ydnhaha/p/9781214.html
时间: 2024-10-10 07:39:45