把一张无向图分为两个点集,使得不同点集的点之间的边数最少,n<=26。
n<=26,所以,可以爆搜,然后用二进制表示某个点是在第一个点集还是在第二个点集。初始化所有点都在点集S2中,然后在爆搜的过程中不断更新最小值。
那么,下面就可以得到一个爆搜程序
#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
const int N=14;
int n,m,x,y,s1,s2,ans,ans1;
int p[N<<1];
inline int getnum(int p)
{
int sum=0;
for (register int i=1; i<=n; ++i) if ((1<<(i-1))&p) sum++;
return sum;
}
void dfs(int x,int now,int sum)
{
if (now==n/2)
{
if (sum<ans) ans=sum,ans1=s1;
return;
}
for (register int i=x+1; i<=n; ++i)
{
int add1=getnum(p[i]&s1);
int add2=getnum(p[i]&s2);
s1|=(1<<(i-1));
s2^=(1<<(i-1));
dfs(i,now+1,sum-add1+add2);
s1^=(1<<(i-1));
s2|=(1<<(i-1));
}
}
int main(){
ans=2e9;
scanf("%d%d",&n,&m);
for (register int i=1; i<=m; ++i)
{
scanf("%d%d",&x,&y);
p[x]|=(1<<(y-1));
p[y]|=(1<<(x-1));
}
s1=0; s2=(1<<n)-1;
dfs(0,0,0);
for (register int i=1; i<=n; ++i) if (ans1&(1<<(i-1))) printf("%d ",i);
return 0;
}
上交后发现,Tle 5个点,爆搜复杂度是正确的呀?c[26][13]十分小。原来,对于每一个状态,都会执行两次O(n)的getnum,所以因为这个大常数,TLE。
那么,我们就把每一个状态产生的贡献预处理一下吧
下面就是预处理的过程。
inline int query(int x)
{
int res=0;
while (x)
{
res++;
x-=lowbit(x);
}
return res;
}
for (register int p=0; p<(1<<26); ++p) s[p]=query(p);
然而,在这个过程中,我们又发现了一个问题:(1<<26)*4/1024/1024=256MB,如果是在NOIP中,一般是512MB内存没有任何问题,但是,洛谷内存限制为125MB,所以,炸内存了。
怎么解决这个问题呢?
设s[p]是p这个数用二进制表示后1的个数,然后,我们假设现在p二进制位数为8,那么可以得到:s[p]=s[p>>4]+s[p-((p>>4)<<4)],就是把8位的总贡献分为前四位和后四位来统计,是不是觉得很神奇。当然,s[p]=s[p>>3]+s[p-((p>>3)<<3)](前5位和后3位),s[p]=s[p>>2]+s[p-((p>>2)<<2)](前6位和后2位)…
所以,对于一个26位的数,我们可以把它分为前1位和后25位,前2位和后24位,前3位和后23位… 那么,我们只要把它分为前13位和后13位来统计,就可以把数组开到1<<13了,当然,你想再开大点,比如分为前14位和后12位等等等等,都没关系。
下面给出AC代码
#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
const int N=14;
int n,m,x,y,s1,s2,ans,ans1;
int p[N<<1],s[1<<N];
inline int query(int x)
{
int res=0;
while (x)
{
res++;
x-=lowbit(x);
}
return res;
}
inline int suan(int x)
{
return s[x>>13]+s[x-((x>>13)<<13)];
}
void dfs(int x,int now,int sum)
{
if (now==n/2)
{
if (sum<ans) ans=sum,ans1=s1;
return;
}
for (register int i=x+1; i<=n; ++i)
{
int add1=suan(p[i]&s1);
int add2=suan(p[i]&s2);
s1|=(1<<(i-1));
s2^=(1<<(i-1));
dfs(i,now+1,sum-add1+add2);
s1^=(1<<(i-1));
s2|=(1<<(i-1));
}
}
int main(){
ans=2e9;
scanf("%d%d",&n,&m);
for (register int p=0; p<(1<<13); ++p) s[p]=query(p);
for (register int i=1; i<=m; ++i)
{
scanf("%d%d",&x,&y);
p[x]|=(1<<(y-1));
p[y]|=(1<<(x-1));
}
s1=0; s2=(1<<n)-1;
dfs(0,0,0);
for (register int i=1; i<=n; ++i) if (ans1&(1<<(i-1))) printf("%d ",i);
return 0;
}