Description
贝希和她的闺密们在她们的牛棚中玩游戏。但是天不从人愿,突然,牛棚的电源跳闸了,所有的灯都被关闭了。贝希是一个很胆小的女生,在伸手不见拇指的无尽的黑暗中,她感到惊恐,痛苦与绝望。她希望您能够帮帮她,把所有的灯都给重新开起来!她才能继续快乐地跟她的闺密们继续玩游戏!
牛棚中一共有N(1 <= N <= 35)盏灯,编号为1到N。这些灯被置于一个非常复杂的网络之中。有M(1 <= M <= 595)条很神奇的无向边,每条边连接两盏灯。
每盏灯上面都带有一个开关。当按下某一盏灯的开关的时候,这盏灯本身,还有所有有边连向这盏灯的灯的状态都会被改变。状态改变指的是:当一盏灯是开着的时候,这盏灯被关掉;当一盏灯是关着的时候,这盏灯被打开。
问最少要按下多少个开关,才能把所有的灯都给重新打开。
数据保证至少有一种按开关的方案,使得所有的灯都被重新打开。
Input
第一行:两个空格隔开的整数:N和M。
第二到第M+1行:每一行有两个由空格隔开的整数,表示两盏灯被一条无向边连接在一起。没有一条边会出现两次。
Output
第一行:一个单独的整数,表示要把所有的灯都打开时,最少需要按下的开关的数目。
Sample Input
5 6
1 2
1 3
4 2
3 4
2 5
5 3
Sample Output
3
Data Constraint
Hint
【样例说明】
一共有五盏灯。灯1、灯4和灯5都连接着灯2和灯3。按下在灯1、灯4和灯5上面的开关。
Solution
这道题看上去无从下手, N≤35 的范围令人有些不知所措。
事实上这道题的解法十分巧妙,用到了 折半搜索 的巧法。
分别搜索 217 和 218 两部分,记录其中一部分的答案,用哈希表储存。
处理另一部分时从哈希表中查找,异或出答案,相加即可。
这样的时间复杂度折半为 2N2 ,可以通过本题。
详细代码见下面:(C++选手可以使用 STL 的 Map 库代替哈希,代码为注释部分,简单却较慢)
据说 这题可以用高斯消元来做,一盏灯与周围灯的异或值为 1 表示亮,列出 N 个方程解之。
Code
#include<cstdio>
//#include<map>
using namespace std;
const int N=36,mo=1234321;
//map<long long,int>mp;
int n,m,m1,m2,ans=N;
int a[N][N];
long long p[N],f[N],g[mo],h[mo];
bool bz[N];
inline int read()
{
int X=0,w=1; char ch=0;
while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
return X*w;
}
inline int min(int a,int b)
{
return a<b?a:b;
}
inline int hash(long long x)
{
int y=x%mo;
while(h[y] && h[y]!=x) y=(y+1)%mo;
return y;
}
inline void dfs1(int x)
{
if(x>m1)
{
long long num=0;
int s=1;
for(int i=1;i<=m1;i++)
if(bz[i]) num^=f[i],s++;
int k=hash(num);
if(!h[k]) h[k]=num,g[k]=s; else
g[k]=min(g[k],s);
/*if(!mp[num]) mp[num]=s; else
mp[num]=min(mp[num],s);*/
return;
}
bz[x]=true;
dfs1(x+1);
bz[x]=false;
dfs1(x+1);
}
inline void dfs2(int x)
{
if(x>m2)
{
long long num=0;
int s=-1;
for(int i=m1+1;i<=m2;i++)
if(bz[i]) num^=f[i],s++;
int k=hash(p[n]-1-num);
if(h[k]) ans=min(ans,g[k]+s);
//if(mp[p[n]-1-num]) ans=min(ans,mp[p[n]-1-num]+s);
return;
}
bz[x]=true;
dfs2(x+1);
bz[x]=false;
dfs2(x+1);
}
int main()
{
freopen("1!.in","r",stdin);
n=read(),m=read();
for(int i=p[0]=1;i<=n;i++) p[i]=p[i-1]<<1;
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
a[x][++a[x][0]]=y;
a[y][++a[y][0]]=x;
}
for(int i=1;i<=n;i++)
{
f[i]=p[i-1];
for(int j=1;j<=a[i][0];j++) f[i]+=p[a[i][j]-1];
}
m1=min(N>>1,m2=n);
//mp[0]=1;
dfs1(1);
dfs2(m1+1);
printf("%d",ans);
return 0;
}