ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P7689 [CEOI2002] Bugs Integrated,Inc. 题解

2022-07-26 11:03:22  阅读:117  来源: 互联网

标签:状态 cnt int 题解 backslash Bugs P7689 突起 include


Solution

感谢同学对这个题的精彩讲解!

设 \(f_{i,S_1,S_2}\) 表示处理完前 \(i\) 行,第 \(i+1\) 行的“突起”状态为 \(S_1\),第 \(i+2\) 行的“突起”状态为 \(S_2\)。

转移时,我们设第 \(i\) 行的状态为 \(S\),第 \(i-1\) 行 “突起”到第 \(\bm{i+1}\) 行 的状态为 \(S_0\),那么我们要从 \(f_{i-1,S,S_0}\) 转移到 \(f_{i,S_1,S_2}\):

\[f_{i,S_1,S_2}=f_{i-1,S,S_0}+\frac{|S_2|}2+\frac{|S_1\backslash S_2\backslash S_0|}3 \]

注:\(S_2\) 即占据了 \(i\sim i+2\) 的 \(3\times 2\) 方块,\(|S_1\backslash S_2\backslash S_0|\) 即占据了 \(i\sim i+1\) 的 \(2\times 3\) 方块,可以自行画图理解。

于是这个东西时间复杂度太高,过不去。

注意到 \(S_0,S_2\) 中连续 \(1\) 的个数必须是偶数,\(|S_1\backslash S_2\backslash S_0|\) 中连续 \(1\) 的个数必须是三的倍数,我们可以只枚举这些符合要求的状态,这样可以节省很多无用枚举。

另外还有很多小的剪枝,比如 \(S_2\subseteq S_1\) 必须成立、\(S_0\subseteq S_1\backslash S_2\) 必须成立等等,具体见代码。

另外为了压空间,需要使用 uint8_tuint16_t 等类型。

Code

#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
using namespace std;

#define count(x) (__builtin_popcount(x))

const int N = 150, M = 10;

int n, m, k;
uint8_t f[2][1 << M][1 << M];
uint16_t a[N + 10];

bool check(int x, int y) {
  int cnt = 0;
  for (int i = 0; i <= (1 << m) - 1; i++) {
    if ((x >> i) & 1) cnt++;
    else {
      if (cnt % y) return 0;
      cnt = 0;
    }
  }
  return 1;
}

void mian() {
  scanf("%d%d%d", &n, &m, &k);
  for (int i = 1; i <= k; i++) {
    int x, y; scanf("%d%d", &x, &y);
    a[x] |= (1 << (y - 1));
  }
  uint16_t tot = (1 << m) - 1;
  vector<uint16_t> ok1; // 2
  a[n + 1] = a[n + 2] = tot;
  set<uint16_t> ok2; // 3
  for (uint16_t i = 0; i <= (1 << m) - 1; i++) {
    if (check(i, 2)) ok1.push_back(i);
    if (check(i, 3)) ok2.insert(i);
  }
  // msk0 msk msk1 msk2
  for (int i = 1, ii = 1; i <= n; i++, ii = i % 2)
    for (auto msk2 : ok1)
      if ((msk2 & a[i + 2]) == 0)
        for (uint16_t msk1 = 0; msk1 <= tot; msk1++)
          if ((msk1 & msk2) == msk2 && (msk1 & a[i + 1]) == 0 && (msk1 & a[i]) == 0)
            for (auto msk0 : ok1)
              if ((msk0 & a[i]) == 0 && (msk0 & a[i - 1]) == 0 && ((msk1 - msk2) & msk0) == msk0 && ok2.find(msk1 - msk2 - msk0) != ok2.end())
                for (uint16_t msk = 0; msk <= tot; msk++)
                  if ((msk & a[i]) == 0 && (msk & a[i - 1]) == 0 && ((msk1 - msk0) & msk) == 0 && (msk1 & msk0) == msk0)
                    f[ii][msk1][msk2] = max(f[ii][msk1][msk2], uint8_t(f[(ii + 1) % 2][msk][msk0] + count(msk2) / 2 + count(msk1 - msk2 - msk0) / 3));
  printf("%d\n", int(f[n % 2][0][0]));
}

int main() {
  int T; scanf("%d", &T);
  while (T--) {
    memset(f, 0, sizeof(f)); memset(a, 0, sizeof(a));
    mian();
  }
  return 0;
}

标签:状态,cnt,int,题解,backslash,Bugs,P7689,突起,include
来源: https://www.cnblogs.com/registergen/p/p7689_solution.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有