ccpc威海 D-Sternhalma(狀壓DP,記憶化搜尋)
阿新 • • 發佈:2022-12-01
題意
- 給定六邊形棋盤每個格子的分數,詢問若干初始的棋子擺放
方式,問按照規則移除棋子最多得多少分。 - 移除棋子有兩種方式,一種是直接移除一個棋子,不得分;
另一種是用一個棋子跳過其相鄰棋子,移除被跳過的棋子並
且得分增加被移除棋子所在的格子的分數。
原題連結
解題思路
棋盤上的每個位置只有放與不放兩種選擇,故最終一共有\(2^{19}\)種狀態。初態已知,那麼我們可以用DP推出後面的所有狀態,也可以用記憶化搜尋。
程式碼細節較多,容易出錯。
DP
#include <bits/stdc++.h> using namespace std; const int N = (1 << 19) + 1; //方向矩陣 const int d1[6][2] = {{0, 2}, {-1, 1}, {-1, -1}, {0, -2}, {1, -1}, {1, 1}}; const int d2[6][2] = {{0, 4}, {-2, 2}, {-2, -2}, {0, -4}, {2, -2}, {2, 2}}; //為每個點給定二維座標 const int coor[19][2] = { {1, 3}, {1, 5}, {1, 7}, {2 ,2}, {2 ,4}, {2 ,6}, {2, 8}, {3, 1}, {3, 3}, {3, 5}, {3, 7}, {3, 9}, {4, 2}, {4 ,4}, {4 ,6}, {4 ,8}, {5, 3}, {5 ,5}, {5 ,7} }; int s[6][10];//點的權值 int id[8][15];//每個區域的編號 int f[N]; //將字串轉化為十進位制的狀態 int trans(string &mp) { int ans = 0; for (int i = 0; i < 19; ++i) { if (mp[i] == '#') ans += (1 << i); } return ans; } int count(int x) { int ans = 0; for (int i = 0; i < 19; ++i) { if (x & 1 << i) ++ans; } return ans; } vector<int> b; void ini() { for (int i = 0; i < (1 << 19); ++i) b.push_back(i); //排序,從小狀態推大狀態 sort(b.begin(), b.end(), [](int a, int b) { return count(a) < count(b); }); for (int i = 1; i < (1 << 19); ++i) { int state = b[i]; for (int j = 0; j < 19; ++j) { if (state & (1 << j)) { int x = coor[j][0], y = coor[j][1]; f[state] = max(f[state], f[state - (1 << j)]); for (int k = 0; k < 6; ++k) { int x1 = x + d1[k][0], y1 = y + d1[k][1]; int x2 = x + d2[k][0], y2 = y + d2[k][1]; if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0) continue; if (id[x1][y1] == -1 || id[x2][y2] == -1) continue; //注意&與==的優先順序 if ((state & 1 << id[x1][y1]) == 0 || (state & 1 << id[x2][y2]) == 1) continue; f[state] = max(f[state], f[state ^ (1 << id[x][y]) ^ (1 << id[x1][y1]) ^ (1 << id[x2][y2])] + s[x1][y1]); } } } } } int main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); memset(id, -1, sizeof(id)); memset(f, -0x3f, sizeof(f)); f[0] = 0; for (int i = 0; i < 19; ++i) { int x = coor[i][0], y = coor[i][1]; id[x][y] = i; cin >> s[x][y]; } ini(); int n; cin >> n; while (n--) { string mp, t; for (int i = 0; i < 5; ++i) cin >> t, mp += t; cout << f[trans(mp)] << endl;; } }
記憶化搜尋
#include <bits/stdc++.h> using namespace std; const int N = (1 << 19) + 1; const int d1[6][2] = {{0, 2}, {-1, 1}, {-1, -1}, {0, -2}, {1, -1}, {1, 1}}; const int d2[6][2] = {{0, 4}, {-2, 2}, {-2, -2}, {0, -4}, {2, -2}, {2, 2}}; const int coor[19][2] = { {1, 3}, {1, 5}, {1, 7}, {2 ,2}, {2 ,4}, {2 ,6}, {2, 8}, {3, 1}, {3, 3}, {3, 5}, {3, 7}, {3, 9}, {4, 2}, {4 ,4}, {4 ,6}, {4 ,8}, {5, 3}, {5 ,5}, {5 ,7} }; int s[6][10];//點的權值 int id[8][15];//每個區域的編號 int f[N]; int trans(string &mp) { int ans = 0; for (int i = 0; i < 19; ++i) { if (mp[i] == '#') ans += (1 << i); } return ans; } int dfs(int state) { if (f[state] != int(0xc1c1c1c1)) return f[state]; int &val = f[state]; int grid[8][15] = {0}; for (int i = 0; i < 19; ++i) { if (state & 1 << i) { int x = coor[i][0], y = coor[i][1]; int n_state = state & ~(1 << i); grid[x][y] = 1; val = max(val, dfs(n_state)); } } for (int i = 0; i < 19; ++i) { if (state & 1 << i) { int x = coor[i][0], y = coor[i][1]; for (int j = 0; j < 6; ++j) { int x1 = x + d1[j][0], y1 = y + d1[j][1]; int x2 = x + d2[j][0], y2 = y + d2[j][1]; if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0) continue; if (!~id[x1][y1] || !~id[x2][y2]) continue; if (!grid[x1][y1] || grid[x2][y2]) continue; int n_state = state; n_state &= ~(1 << i); n_state &= ~(1 << id[x1][y1]); n_state |= (1 << id[x2][y2]); val = max(val, dfs(n_state) + s[x1][y1]); } } } return val; } int main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); memset(id, -1, sizeof(id)); memset(f, -0x3f, sizeof(f)); f[0] = 0; for (int i = 0; i < 19; ++i) { int x = coor[i][0], y = coor[i][1]; id[x][y] = i; cin >> s[x][y]; } int n; cin >> n; while (n--) { string mp, t; for (int i = 0; i < 5; ++i) cin >> t, mp += t; cout << dfs(trans(mp)) << endl;; } }