3月18日¶
24湖北省赛H¶
去年比赛没时间看,虽然看了也肯定不会写。偶然翻题单在状压DP里翻到,,但是我连啥叫状压DP都不太记得了,之前只看过几题。
其实是昨天先看了下题解,大概知道要用状压 DP ,然后每两位表示一个有鱼的单元格的状态。
因为每个池塘的鱼的数量最多只有3,所以我们用2位二进制位就可以表示出来。总共需要 \(2k\) 位。由于 \(k\) 很小,所以就可以这样。
并且还有一个很关键的信息就是,每个炸弹最多只能炸五个单元格,并且形状是固定的。所以每个池塘最多只会被5个单元格给炸到。
所以总共最多只有 \(5k\) 个池塘有可能被放置炸弹。我们枚举每一个状态,看使用每一个炸弹会转移到什么状态。
最后我们需要输出鱼全都被炸似的最小的答案,也就是 \(dp_0\) 。
int mp[1001][1001];
void ChatGptDeepSeek()
{
memset(mp, -1, sizeof mp);
// cerr<<mp[0][0]<<'\n';
int n, m, k;
cin >> n >> m >> k;
vector<int> x(k), y(k), a(k);
int now = 0;
// map<pair<int, int>, int> mp;
for (int i = 0; i < k; i++) {
cin >> x[i] >> y[i] >> a[i];
mp[x[i]][y[i]] = i;
// mp[{ x[i], y[i] }] = i;
now |= (a[i] << (2 * i));
}
// 10 01 10
// cerr << now << '\n';
vector<int> dp(now + 1, 666);
dp[now] = 0;
vector<pair<int, int>> dir { { 0, 0 }, { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } };
auto calc = [&](int now_val, int i, int j) {
for (auto it : dir) {
int nx = i + it.first, ny = j + it.second;
if (nx > n || nx < 1 || ny > m || ny < 1)
continue;
if (mp[nx][ny] != -1) {
int xx = (now_val >> (2 * mp[nx][ny])) & 3;
assert(xx <= 3 && xx >= 0);
now_val ^= (xx << (2 * mp[nx][ny]));
xx = max(0, xx - 1);
now_val |= (xx << (2 * mp[nx][ny]));
}
}
return now_val;
};
//k * 2**20
//k*1e6*5
for (int i = 0; i < k; i++) {
for (auto it : dir) {
int nx = x[i] + it.first, ny = y[i] + it.second;
if (nx > n || nx < 1 || ny > m || ny < 1)
continue;
// for (int _ = 1; _ <= 3; _++)
for (int j = now; j >= 0; j--) {
int nxt = calc(j, nx, ny);
assert(nxt <= j);
dp[nxt] = min(dp[nxt], dp[j] + 1);
}
}
}
cout << dp[0] << '\n';
}
然后其实就是一些简单的操作了。。但是我把 map 改成数组就不超时了。。
但是时间最多 500ms 了,算比较慢的了。看了别人的更快代码发现,有的是 DFS ,有的是把状态放外层,如果不是合法状态就跳过,可以减少很多的时间。
改了之后变成 240ms。。虽然写了十分钟多,且写错了看了一会才看出来。。我把 ny>m || ny<1
写成了 ny>m || ny>m
。
for (int i = now; i >= 0; i--) {
{
int tmp = i;
bool skip = false;
for (int j = 0; j < k; j++) {
if ((tmp & 3) > a[j]) {
skip = true;
break;
}
tmp >>= 2;
}
if (skip)
continue;
}
// cerr<<i<<" "<<dp[i]<<" \n";
for (int j = 0; j < k; j++) {
for (auto it : dir) {
int nx = x[j] + it.first, ny = y[j] + it.second;
if (nx > n || nx < 1 || ny > m || ny < 1)
continue;
int nxt = calc(i, nx, ny);
// cerr<<nxt<<'\n';
dp[nxt] = min(dp[nxt], dp[i] + 1);
}
}
}
24湖北省赛G¶
乐,写了三个小时。。。虽然前面一直在写错误的做法。。
最后精力不太行了,实在看不出来。。问了下deepseek,帮我找到了一些问题。因为写错了重新写一下,然后忘记初始化并查集了。。
然后占了新格子的话,周围所有的点的气都不能有这个格子。这一点我也知道啊,但是脑子非常混乱了。。然后瞎改了下过了。
还是很麻烦的一个题的,然后我是没有想到每个连通块的气都放集合里不会TLE MLE。。。但是每个连通块每次 DFS BFS算气也是没问题。。
确实就是模拟。不然我也写不了这么久,可能就一点东西都敲不了。
感觉 BFS DFS 同样不好写。真要哈气了。
虽然效率很低,但也是多写一题吧。也该休息一下了。
int board[20][20];
int p[20][20];
constexpr int N = 5e5;
int cnt[N + 1], f[N + 1];
// bool vis[N + 1];
// vector<int> f(9);
int find(int x)
{
return f[x] == x ? x : f[x] = find(f[x]);
}
void ChatGptDeepSeek()
{
// 看自己上下左右的同色的
// 如果有 那么加入它们所在的联通块
// 用并查集维护联通的信息
//
// 看它周围的颜色不同的 若气为0了 则该全部移除了
// 怎么用并查集呢?好像还行 试试 ,其实没用过几次并查集(
for (int i = 0; i <= 19; i++)
for (int j = 0; j <= 19; j++)
board[i][j] = -1;
int m;
cin >> m;
vector<pair<int, int>> dir { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
vector<set<pair<int, int>>> Qi(m + 1, set<pair<int, int>>());
int res = 0;
auto Eat = [&](auto&& self, int x, int y, int v) -> void {
// cerr << x << " " << y << '\n';
res++;
assert(x <= 19 && x >= 1 && y <= 19 && y >= 1);
board[x][y] = -1;
for (auto it : dir) {
int nx = x + it.first, ny = y + it.second;
if (nx > 19 || nx < 1 || ny > 19 || ny < 1 || board[nx][ny] == -1)
continue;
if (board[nx][ny] != v) {
int idx = find(p[nx][ny]);
assert(idx <= m && idx > 0);
Qi[idx].insert({ x, y });
} else
self(self, nx, ny, v);
}
};
for (int i = 1; i <= m; i++) {
res = 0;
int x, y;
cin >> x >> y;
board[x][y] = (i % 2);
p[x][y] = i;
f[i] = i;
// 先算周围的反色的气
for (auto it : dir) {
int nx = x + it.first, ny = y + it.second;
if (nx > 19 || nx < 1 || ny > 19 || ny < 1 || board[nx][ny] == -1)
continue;
int idx = find(p[nx][ny]);
// if(!idx) continue;
assert(idx <= m && idx > 0);
if (!Qi[idx].empty())
Qi[idx].erase({ x, y });
if (p[nx][ny] % 2 != i % 2) {
// dbg(i, nx, ny);
if (Qi[idx].empty()) {
// dbg(nx, ny, idx);
Eat(Eat, nx, ny, p[nx][ny] % 2);
// f[idx] = 0;
}
}
}
for (auto it : dir) {
int nx = x + it.first, ny = y + it.second;
if (nx > 19 || nx < 1 || ny > 19 || ny < 1)
continue;
if (board[nx][ny] == -1)
Qi[i].insert({ nx, ny });
}
for (auto it : dir) {
int nx = x + it.first, ny = y + it.second;
if (nx > 19 || nx < 1 || ny > 19 || ny < 1 || (board[nx][ny] != i % 2))
continue;
int idx = find(p[nx][ny]);
if (idx == i)
continue;
assert(idx <= m && idx > 0 && board[nx][ny] == board[x][y]);
for (auto it : Qi[idx]) {
// if (board[it.first][it.second] == -1)
Qi[i].insert(it);
}
f[idx] = i;
}
// dbg(Qi[i]);
// cerr << Qi[i].size() << '\n';
int val = res;
res = 0;
if (Qi[i].empty())
Eat(Eat, x, y, i & 1);
if (i & 1) {
cout << res << " " << val << '\n';
} else {
cout << val << " " << res << '\n';
}
// dbg(Qi[find(1)]);
// dbg(Qi[2]);
}
// 我要哈气了
}