P2962 [USACO09NOV]灯Lights [高斯消元+异或方程组 / 折半搜索]

L i g h t s Lights Lights

节日宴会上,我们有 N(10<=N<=36)盏彩色灯,他们分别从 1 到 N 被标上号码。有 M 条边连接着这些灯,当按下某一盏灯的开关的时候,这盏灯本身以及所有和这盏灯有边相连的灯的开关状态都会发生改变。最开始所有灯都是被关着的,问需要至少按下多少开关,才可以把所有灯打开。

1 ≤ N ≤ 36 1 \le N \le 36 1N36.


正 解 部 分 \color{red}{正解部分}

方 法 1 : 方法 1: 1:
这道题目 建立异或方程组, 对自由元的状态进行枚举, 解出方程, 从中选出最优解.

方 法 2 : 方法 2: 2:
折半搜索,

将原来的序列分为 N / 2 N/2 N/2 N − N / 2 N - N/2 NN/2 两部分,

始状态 搜出操作前半部分能得到的所有状态, 记为 S 1 S_1 S1,
末状态 倒搜出操作后半部分能得到的所有状态, 记为 S 2 S_2 S2,

S 2 S_2 S2 中查找每个 S 1 S_1 S1 中的元素, 找到则更新答案 .


实 现 部 分 \color{red}{实现部分}

方 法 1 : 方法 1: 1:

待 填 坑 . \color{red}{待填坑 .} .

方 法 2 : 方法 2: 2:

注意 D F S _ 1 DFS\_1 DFS_1 D F S _ 2 DFS\_2 DFS_2 不要搞混 .
code\ with\ bug

使用状态压缩:

  • 每个操作都可以使用二进制状态表示, 使用时直接对状态异或就可以方便地改变状态.
  • 使用 m a p map map 保存状态.
  • 注意自环 .
#include<bits/stdc++.h>
#define reg register
typedef long long ll;

const int maxn = 300005;

int N;
int M;
int Ans;

ll Opt[maxn];

std::map <ll, int> Mp;

void DFS(int k, int cnt, ll zt){
        if(k == N+1){
                if(Mp.count(zt)) Ans = std::min(Ans, cnt + Mp[zt]);
                return ;
        }
        DFS(k+1, cnt+1, zt^Opt[k]), DFS(k+1, cnt, zt);
}

int main(){
        scanf("%d%d", &N, &M);
        for(reg int i = 1; i <= M; i ++){
                int a, b;
                scanf("%d%d", &a, &b);
                Opt[a] |= (1ll<<b-1), Opt[b] |= (1ll<<a-1);
        }
        for(reg int i = 1; i <= N; i ++) Opt[i] |= (1ll<<i-1);
        int Half = N >> 1;
        for(reg int i = 0; i < 1<<Half; i ++){
                int cnt = 0;
                ll zt = 0;
                for(reg int j = 1; j <= Half; j ++)
                        if((1ll<<j-1) & i) cnt ++, zt ^= Opt[j];
                if(Mp.count(zt)) Mp[zt] = std::min(Mp[zt], cnt);
                else Mp[zt] = cnt;
        }
        Ans = 0x3f3f3f3f;
        DFS(Half+1, 0, (1ll<<N)-1);
        printf("%d\n", Ans);
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值