【题解】JZOJ1321:灯

版权声明:Fashion Education https://blog.csdn.net/ModestCoder_/article/details/81866190

我参考了这篇题解
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.

猜你喜欢

转载自blog.csdn.net/ModestCoder_/article/details/81866190