P8422 [THUPC2022 决赛] 德州消消乐 题解

比较好做的一道大模拟,没有什么恶心的细节。

我们一个一个来实现操作。

辅助函数

bool I(int x, int y) { return x >= 1 && x <= n && y >= 1 && y <= m; }
bool N(int x, int y) { return !I(x, y) || !a[x][y].first; }

I(x,y) 用来判断坐标是否在范围内,N(x,y) 用来判断坐标是否合法。

int Cx(int x, int y) {
  int cx = 0;
  for (int _x = x; _x >= 1 && a[x][y].first == a[_x][y].first; --_x, ++cx) {
  }
  for (int _x = x + 1; _x <= n && a[x][y].first == a[_x][y].first; ++_x, ++cx) {
  }
  return cx;
}
int Cy(int x, int y) {
  int cy = 0;
  for (int _y = y; _y >= 1 && a[x][y].first == a[x][_y].first; --_y, ++cy) {
  }
  for (int _y = y + 1; _y <= m && a[x][y].first == a[x][_y].first; ++_y, ++cy) {
  }
  return cy;
}
void T(int x, int y) { v[x][y] = a[x][y].first; }

Cx(x,y),Cy(x,y) 用处类似,都是用来统计同一行/列中连续的棋子数量。

T(x,y) 用来打标记,表示这个坐标是需要被删除的。

交换棋子

bool H(int x, int y) { return !N(x, y) && (Cx(x, y) >= 3 || Cy(x, y) >= 3); }
bool C(int x1, int y1, int x2, int y2) {
  if (N(x1, y1) || N(x2, y2) || abs(x1 - x2) + abs(y1 - y2) != 1) {
    return 0;
  }
  swap(a[x1][y1], a[x2][y2]);
  bool f = H(x1, y1) || H(x2, y2);
  swap(a[x1][y1], a[x2][y2]);
  return f;
}

H(x,y) 用来判断当前点能否被消除,C(x1,y1,x2,y2) 用来判断此次交换是否有效。

交换有效的条件:两个坐标都合法,曼哈顿距离为 \(1\),交换后可以被消除。

寻找被消除的棋子

坑点:被消除的棋子是从当前坐标开始向行/列延伸的所有同色棋子,而不是同色四连通块。

void G(int x, int y) {
  if (Cx(x, y) >= 3) {
    for (int _x = x; _x >= 1 && a[x][y].first == a[_x][y].first; T(_x, y), --_x) {
    }
    for (int _x = x + 1; _x <= n && a[x][y].first == a[_x][y].first; T(_x, y), ++_x) {
    }
  }
  if (Cy(x, y) >= 3) {
    for (int _y = y; _y >= 1 && a[x][y].first == a[x][_y].first; T(x, _y), --_y) {
    }
    for (int _y = y + 1; _y <= m && a[x][y].first == a[x][_y].first; T(x, _y), ++_y) {
    }
  }
}
bool G() {
  bool f = 0;
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      if (H(i, j)) {
        G(i, j), f = 1;
      }
    }
  }
  return f;
}

G(x,y) 会对从当前坐标开始会被删除的棋子全部打上标记;G() 会找出所有要被删除的棋子,同时判断是否还要继续删。

消除棋子

void C(int x, int y) {
  if (N(x, y)) {
    return;
  }
  int ty = a[x][y].second, co = a[x][y].first;
  sc += co, a[x][y] = {0, 0};
  if (ty == 1 || ty == 3) {
    for (int k = 1; k <= m; ++k) {
      C(x, k);
    }
  }
  if (ty == 2 || ty == 3) {
    for (int k = 1; k <= n; ++k) {
      C(k, y);
    }
  }
  if (ty == 6) {
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= m; ++j) {
        if (a[i][j].first == co) {
          C(i, j);
        }
      }
    }
  }
  if (ty == 4 || ty == 5) {
    int v = ty - 3;
    for (int i = -v; i <= v; ++i) {
      for (int j = -v; j <= v; ++j) {
        if (i || j) {
          C(x + i, y + j);
        }
      }
    }
  }
}

消除的时候记录一下颜色编号和用来结算分数,剩下的就是按照题意模拟。

主过程

void FD() {
  for (int j = 1; j <= m; ++j) {
    int k = n;
    for (int i = n; i >= 1; --i) {
      if (a[i][j].first) {
        a[k--][j] = a[i][j];
      }
    }
    for (; k; a[k--][j] = {0, 0}) {
    }
  }
}
......
for (int x1, y1, x2, y2; q--;) {
  cin >> x1 >> y1 >> x2 >> y2;
  bool f = C(x1, y1, x2, y2);
  if (!f) {
    aC = 0;
    continue;
  }
  swap(a[x1][y1], a[x2][y2]);
  int k = 0;
  for (; G(); FD()) {
    ++k, sc = 0;
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= m; ++j) {
        if (v[i][j]) {
          C(i, j);
        }
      }
    }
  }
}

模拟每一轮的消除和下落即可

结算分数

终局奖分

很简单,开个变量存一下所有操作的合法性,最后判一下清空盘面即可。

if (aC) {
  ans += 1000;
}
aC = 1;
for (int i = 1; i <= n; ++i) {
  for (int j = 1; j <= m; ++j) {
    aC &= !a[i][j].first;
  }
}
if (aC) {
  ans += 10000;
}

消除奖分

维护一下之前提到的 sc,每消除后结算分数即可。

for (int x1, y1, x2, y2; q--;) {
  cin >> x1 >> y1 >> x2 >> y2;
  bool f = C(x1, y1, x2, y2);
  if (!f) {
    aC = 0;
    continue;
  }
  swap(a[x1][y1], a[x2][y2]);
  int k = 0;
  for (; G(); FD()) {
    ++k, sc = 0;
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= m; ++j) {
        if (v[i][j]) {
          C(i, j);
        }
      }
    }
    ans += sc * k; // 消除奖分
  }
}

连锁奖分

记一下总轮数即可。

for (int x1, y1, x2, y2; q--;) {
  cin >> x1 >> y1 >> x2 >> y2;
  bool f = C(x1, y1, x2, y2);
  if (!f) {
    aC = 0;
    continue;
  }
  swap(a[x1][y1], a[x2][y2]);
  int k = 0;
  for (; G(); FD()) {
    ++k, sc = 0;
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= m; ++j) {
        if (v[i][j]) {
          C(i, j);
        }
      }
    }
    ans += sc * k; // 消除奖分
  }
  ans += 80 * (k - 1) * (k - 1); // 连锁奖分
}

组合奖分

找出所有的同色四连通块,结算分数即可。

int F(int x, int y, int co) {
  if (!I(x, y) || v[x][y] != co) {
    return 0;
  }
  int c = 1;
  v[x][y] = 0;
  for (int k = 0; k < 4; ++k) {
    c += F(x + kD[k][0], y + kD[k][1], co);
  }
  return c;
}
......
for (int x1, y1, x2, y2; q--;) {
  cin >> x1 >> y1 >> x2 >> y2;
  bool f = C(x1, y1, x2, y2);
  if (!f) {
    aC = 0;
    continue;
  }
  swap(a[x1][y1], a[x2][y2]);
  int k = 0;
  for (; G(); FD()) {
    ++k, sc = 0;
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= m; ++j) {
        if (v[i][j]) {
          C(i, j);
        }
      }
    }
    ans += sc * k; // 消除奖分
    for (int i = 1; i <= n; ++i) { // 组合奖分
      for (int j = 1; j <= m; ++j) {
        if (v[i][j]) {
          int vc = F(i, j, v[i][j]);
          if (vc) {
            ans += 50 * (vc - 3) * (vc - 3);
          }
        }
      }
    }
  }
  ans += 80 * (k - 1) * (k - 1); // 连锁奖分
}

牌型奖分

每五次操作结算一下分数即可,可以暴力枚举。

int C(int l[5]) {
  fill_n(cc + 1, k, 0);
  for (int i = 0; i < 5; ++i) {
    ++cc[l[i]];
  }
  vector<int> cl[6];
  for (int i = 0; i < 6; ++i) {
    cl[i].clear();
  }
  for (int i = 1; i <= k; ++i) {
    if (cc[i]) {
      cl[cc[i]].push_back(i);
    }
  }
  if (cl[1].size() == 5) {
    return 50 + cl[1].back();
  }
  if (cl[1].size() == 3) {
    return 100 + cl[2][0] * 2;
  }
  if (cl[1].size() == 2) {
    return 300 + cl[3][0] * 3;
  }
  if (cl[2].size() == 2) {
    return 200 + cl[2][1] * 2 + cl[2][0];
  }
  if (cl[2].size() == 1) {
    return 500 + cl[3][0] * 3 + cl[2][0];
  }
  if (cl[4].size() == 1) {
    return 750 + cl[4][0] * 5;
  }
  return 1000 + cl[5][0] * 10;
}
......
for (int x1, y1, x2, y2; q--;) {
  cin >> x1 >> y1 >> x2 >> y2;
  bool f = C(x1, y1, x2, y2);
  if (!f) {
    aC = 0;
    continue;
  }
  swap(a[x1][y1], a[x2][y2]);
  ml[mlc][0] = H(x1, y1) ? a[x1][y1].first : a[x2][y2].first;
  ml[mlc][1] = H(x2, y2) ? a[x2][y2].first : a[x1][y1].first;
  if (++mlc == 5) {
    mlc = 0;
    int mc = 0;
    for (int i = 0; i < 32; ++i) {
      int c[5] = {0};
      for (int j = 0; j < 5; ++j) {
        c[j] = ml[j][i >> j & 1];
      }
      mc = max(mc, C(c));
    }
    ans += mc; // 牌型奖分
  }
  int k = 0;
  for (; G(); FD()) {
    ++k, sc = 0;
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= m; ++j) {
        if (v[i][j]) {
          C(i, j);
        }
      }
    }
    ans += sc * k; // 消除奖分
    for (int i = 1; i <= n; ++i) { // 组合奖分
      for (int j = 1; j <= m; ++j) {
        if (v[i][j]) {
          int vc = F(i, j, v[i][j]);
          if (vc) {
            ans += 50 * (vc - 3) * (vc - 3);
          }
        }
      }
    }
  }
  ans += 80 * (k - 1) * (k - 1); // 连锁奖分
}

总代码

#include <iostream>

using namespace std;
using Pii = pair<int, int>;

const int kN = 51, kK = 101;
const int kD[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

int n, m, k, q, cc[kK], v[kN][kN], mlc, ml[5][2], ans, sc;
Pii a[kN][kN];
bool aC = 1;

bool I(int x, int y) { return x >= 1 && x <= n && y >= 1 && y <= m; }
bool N(int x, int y) { return !I(x, y) || !a[x][y].first; }
int Cx(int x, int y) {
  int cx = 0;
  for (int _x = x; _x >= 1 && a[x][y].first == a[_x][y].first; --_x, ++cx) {
  }
  for (int _x = x + 1; _x <= n && a[x][y].first == a[_x][y].first; ++_x, ++cx) {
  }
  return cx;
}
int Cy(int x, int y) {
  int cy = 0;
  for (int _y = y; _y >= 1 && a[x][y].first == a[x][_y].first; --_y, ++cy) {
  }
  for (int _y = y + 1; _y <= m && a[x][y].first == a[x][_y].first; ++_y, ++cy) {
  }
  return cy;
}
void T(int x, int y) { v[x][y] = a[x][y].first; }
void G(int x, int y) {
  if (Cx(x, y) >= 3) {
    for (int _x = x; _x >= 1 && a[x][y].first == a[_x][y].first; T(_x, y), --_x) {
    }
    for (int _x = x + 1; _x <= n && a[x][y].first == a[_x][y].first; T(_x, y), ++_x) {
    }
  }
  if (Cy(x, y) >= 3) {
    for (int _y = y; _y >= 1 && a[x][y].first == a[x][_y].first; T(x, _y), --_y) {
    }
    for (int _y = y + 1; _y <= m && a[x][y].first == a[x][_y].first; T(x, _y), ++_y) {
    }
  }
}
bool H(int x, int y) { return !N(x, y) && (Cx(x, y) >= 3 || Cy(x, y) >= 3); }
bool C(int x1, int y1, int x2, int y2) {
  if (N(x1, y1) || N(x2, y2) || abs(x1 - x2) + abs(y1 - y2) != 1) {
    return 0;
  }
  swap(a[x1][y1], a[x2][y2]);
  bool f = H(x1, y1) || H(x2, y2);
  swap(a[x1][y1], a[x2][y2]);
  return f;
}
void C(int x, int y) {
  if (N(x, y)) {
    return;
  }
  int ty = a[x][y].second, co = a[x][y].first;
  sc += co, a[x][y] = {0, 0};
  if (ty == 1 || ty == 3) {
    for (int k = 1; k <= m; ++k) {
      C(x, k);
    }
  }
  if (ty == 2 || ty == 3) {
    for (int k = 1; k <= n; ++k) {
      C(k, y);
    }
  }
  if (ty == 6) {
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= m; ++j) {
        if (a[i][j].first == co) {
          C(i, j);
        }
      }
    }
  }
  if (ty == 4 || ty == 5) {
    int v = ty - 3;
    for (int i = -v; i <= v; ++i) {
      for (int j = -v; j <= v; ++j) {
        if (i || j) {
          C(x + i, y + j);
        }
      }
    }
  }
}
bool G() {
  bool f = 0;
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      if (H(i, j)) {
        G(i, j), f = 1;
      }
    }
  }
  return f;
}
void FD() {
  for (int j = 1; j <= m; ++j) {
    int k = n;
    for (int i = n; i >= 1; --i) {
      if (a[i][j].first) {
        a[k--][j] = a[i][j];
      }
    }
    for (; k; a[k--][j] = {0, 0}) {
    }
  }
}
int C(int l[5]) {
  fill_n(cc + 1, k, 0);
  for (int i = 0; i < 5; ++i) {
    ++cc[l[i]];
  }
  vector<int> cl[6];
  for (int i = 0; i < 6; ++i) {
    cl[i].clear();
  }
  for (int i = 1; i <= k; ++i) {
    if (cc[i]) {
      cl[cc[i]].push_back(i);
    }
  }
  if (cl[1].size() == 5) {
    return 50 + cl[1].back();
  }
  if (cl[1].size() == 3) {
    return 100 + cl[2][0] * 2;
  }
  if (cl[1].size() == 2) {
    return 300 + cl[3][0] * 3;
  }
  if (cl[2].size() == 2) {
    return 200 + cl[2][1] * 2 + cl[2][0];
  }
  if (cl[2].size() == 1) {
    return 500 + cl[3][0] * 3 + cl[2][0];
  }
  if (cl[4].size() == 1) {
    return 750 + cl[4][0] * 5;
  }
  return 1000 + cl[5][0] * 10;
}
int F(int x, int y, int co) {
  if (!I(x, y) || v[x][y] != co) {
    return 0;
  }
  int c = 1;
  v[x][y] = 0;
  for (int k = 0; k < 4; ++k) {
    c += F(x + kD[k][0], y + kD[k][1], co);
  }
  return c;
}

int main() {
  ios_base::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m >> k >> q;
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      cin >> a[i][j].first;
    }
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      cin >> a[i][j].second;
    }
  }
  for (int x1, y1, x2, y2; q--;) {
    cin >> x1 >> y1 >> x2 >> y2;
    bool f = C(x1, y1, x2, y2);
    if (!f) {
      aC = 0;
      continue;
    }
    swap(a[x1][y1], a[x2][y2]);
    ml[mlc][0] = H(x1, y1) ? a[x1][y1].first : a[x2][y2].first;
    ml[mlc][1] = H(x2, y2) ? a[x2][y2].first : a[x1][y1].first;
    if (++mlc == 5) {
      mlc = 0;
      int mc = 0;
      for (int i = 0; i < 32; ++i) {
        int c[5] = {0};
        for (int j = 0; j < 5; ++j) {
          c[j] = ml[j][i >> j & 1];
        }
        mc = max(mc, C(c));
      }
      ans += mc;
    }
    int k = 0;
    for (; G(); FD()) {
      ++k, sc = 0;
      for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
          if (v[i][j]) {
            C(i, j);
          }
        }
      }
      ans += sc * k;
      for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
          if (v[i][j]) {
            int vc = F(i, j, v[i][j]);
            if (vc) {
              ans += 50 * (vc - 3) * (vc - 3);
            }
          }
        }
      }
    }
    ans += 80 * (k - 1) * (k - 1);
  }
  if (aC) {
    ans += 1000;
  }
  aC = 1;
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      aC &= !a[i][j].first;
    }
  }
  if (aC) {
    ans += 10000;
  }
  cout << ans;
  return 0;
}
posted @ 2023-03-07 22:39  bykem  阅读(114)  评论(0)    收藏  举报