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;
}

浙公网安备 33010602011771号