搜尋之meet in middle(有效的小方法)

SSZX_loser_lcy發表於2024-08-09

題目:[https://www.luogu.com.cn/problem/P2962] (P2962 [USACO09NOV] Lights G)

演算法:meet in middle(折半搜尋)

思路:

\(35\)個點,總共的操作狀態有\(2^{35}\)種情況。如果我們採用一般的搜尋方式,時間上會毫不猶豫得爆掉。 所以,我們要用折半搜尋的方式。將所有的點拆分成兩個集合,對兩個集合分別用搜尋的方式(列舉所有可能出現的情況)。這樣就只有\(O(2^{n/2})\)的時間複雜度了!也能順利解決問題!

注意的細節:

1、用 $ long long $ 代替 \(int\) 用來提高資料的承受能力。
2、提前預處理 \(2\) 的指數。
3、用 \(count\) 函式來計算\(1\)的個數。
4、對 << 與 >> 的正確使用。

點選檢視程式碼
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>

using namespace std;

int n, m, ans = 0x7fffffff;
map<long long, int> f;
long long a[40];

int main() {
  cin >> n >> m;
  a[0] = 1;
  for (int i = 1; i < n; ++i) a[i] = a[i - 1] * 2;  // 進行預處理

  for (int i = 1; i <= m; ++i) {  // 對輸入的邊的情況進行處理
    int u, v;
    cin >> u >> v;
    --u;
    --v;
    a[u] |= ((long long)1 << v);
    a[v] |= ((long long)1 << u);
  }

  for (int i = 0; i < (1 << (n / 2)); ++i) {  // 對前一半進行搜尋
    long long t = 0;
    int cnt = 0;
    for (int j = 0; j < n / 2; ++j) {
      if ((i >> j) & 1) {
        t ^= a[j];
        ++cnt;
      }
    }
    if (!f.count(t))
      f[t] = cnt;
    else
      f[t] = min(f[t], cnt);
  }

  for (int i = 0; i < (1 << (n - n / 2)); ++i) {  // 對後一半進行搜尋
    long long t = 0;
    int cnt = 0;
    for (int j = 0; j < (n - n / 2); ++j) {//注意這裡的處理
      if ((i >> j) & 1) {
        t ^= a[n / 2 + j];//連鎖
        ++cnt;
      }
    }
    if (f.count((((long long)1 << n) - 1) ^ t))
      ans = min(ans, cnt + f[(((long long)1 << n) - 1) ^ t]);
  }

  cout << ans;

  return 0;
}

相關文章