我参考了这篇题解
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
【题解】
首先可以知道每个开关最多按1次
那么可以dfs枚举每个开关是否按过,时间复杂度O(2^n)
因为n<=35,所以采用折半搜索,状态用二进制压缩后,再用hash存起来
后半部分的搜索完成后与对应的前半部分答案进行综合
Code:
const mo = 1234321;
var
choose:array[0..10000] of boolean;
edge:array[0..1000000] of record
t,next:longint;
end;
power,head,h,dp,e:array[0..2000000] of int64;
num,n,m,x,y,ans,n1,n2:int64;
i,j:longint;
procedure add(x,y:longint);
begin
inc(num);
edge[num].t := y;
edge[num].next := head[x];
head[x] := num;
end;
function hash(x:int64):int64;
begin
hash := x mod mo;
while (h[hash] <> 0) and (h[hash] <> x) do hash := (hash + 1) mod mo;
end;
function min(x,y:longint):longint;
begin
if x < y then exit(x) else exit(y);
end;
procedure dfs1(t:longint);
var
x,sum,y:int64;
i:longint;
begin
if t > n1 then
begin
x := 0;
sum := 0;
for i := 1 to n1 do
if choose[i] then
begin
x := x xor e[i]; inc(sum);
end;
y := hash(x);
if h[y] = 0 then
begin
h[y] := x;
dp[y] := sum;
end else dp[y] := min(dp[y], sum);
exit;
end;
choose[t] := true;
dfs1(t + 1);
choose[t] := false;
dfs1(t + 1);
end;
procedure dfs2(t:longint);
var
x,sum,y:int64;
i:longint;
begin
if t > n then
begin
x := 0;
sum := 0;
for i := n1 + 1 to n do
if choose[i] then
begin
x := x xor e[i]; inc(sum);
end;
y := hash(power[n] - 1 - x);
if h[y] > 0 then
ans := min(ans,dp[y] + sum);
exit;
end;
choose[t] := true;
dfs2(t + 1);
choose[t] := false;
dfs2(t + 1);
end;
begin
assign(input,'lights.in'); reset(input);
assign(output,'lights.out'); rewrite(output);
readln(n,m);
for i := 1 to m do
begin
readln(x,y);
add(x,y); add(y,x);//可以用邻接矩阵存图,我用了邻接表
end;
power[0] := 1;
for i := 1 to 36 do power[i] := power[i-1] << 1;
for i := 1 to n do
begin
j := head[i];
while j <> 0 do
begin
e[i] := e[i] + power[edge[j].t - 1];
j := edge[j].next;
end;
e[i] := e[i] + power[i-1];
end;
dp[0] := 1;
n1 := min(18,n);
n2 := n;
ans := maxlongint;
dfs1(1);
dfs2(n1 + 1);
writeln(ans);
close(input); close(output);
end.