meet in the middle 折半搜索 刷题记录

复杂度分析

假设本来是n层,本来复杂度是O(2^n),如果meet in middle那就是n/2层,那复杂度变为O( 2^(n/2) ),跟原来的复杂度相比就相当于开了个方

比如如果n=40那爆搜2^40肯定T飞,那用meet in middle的话就是2^20就可做了。

洛谷P2962 [USACO09NOV]灯Lights

  • 灯只有35个,用二进制可以表示所有灯的状态,于是考虑搜索
  • 1表示该灯是亮的,0表示是灭的
  • 把某一个等和与他相邻的灯的位都置1表示该灯位置的开关,用 li 数组表示,按下开关 i 就相当于异或 li[i]
  • map存前半部分能到达的状态下按的最少开关数
  • 先爆搜前半部分,更新map,再爆搜后半部分,看其补集是否存在更新答案
  • 注意前半部分本来就有不需要按灯的状态,所以cnt一开始都是1,最后ans-2即可
  • 基本上抄袭hzwer代码:
     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 typedef long long ll;
     5 int n, m, flag=1, ans=1e9;
     6 ll x[40], li[40];
     7 ll e=0;
     8 map <ll,int> st;
     9 
    10 void dfs(int now, ll d, int cnt) {
    11     if(now == n/2+1 && flag) {
    12         if( st[d]!=0 ) st[d] = min(st[d], cnt);
    13         else st[d] = cnt;
    14         return;
    15     }
    16     if(now == n+1) {
    17         if( st[e-d]!=0 ) ans = min(ans, st[e-d]+cnt);
    18         return;
    19     }
    20     dfs(now+1, d^li[now], cnt+1); //按下开关
    21     dfs(now+1, d, cnt);
    22 }
    23 
    24 int main(){
    25     cin >> n >> m;
    26     x[1] = 1;
    27     for (int i=2; i<=n; i++) x[i] = x[i-1] << 1 ;
    28     int a, b;
    29     for (int i=0; i<m; i++) {
    30         scanf("%d%d", &a, &b);
    31         li[a] ^= x[b];
    32         li[b] ^= x[a];
    33     }
    34     for (int i=1; i<=n; i++) { li[i] ^= x[i]; e ^= x[i]; }
    35     dfs(1,0,1);
    36     flag = 0;
    37     dfs(n/2+1,0,1);
    38     cout << ans-2 << endl;
    39     return 0;
    40 }
    (>人<;)

猜你喜欢

转载自www.cnblogs.com/jiecaoer/p/11707285.html