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

题目:[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;
}
posted @ 2024-08-09 12:47  SSZX_loser_lcy  阅读(7)  评论(0编辑  收藏  举报