洛谷题单指南-状态压缩动态规划-P10865 [HBCPC2024] Genshin Impact Startup Forbidden III
原题链接:https://www.luogu.com.cn/problem/P10865
题意解读:n*m的区域,每个格子里最多有3条鱼,炸弹能覆盖自己以及上下左右5个格子,一个炸弹能炸掉一个格子的一条鱼,求炸掉所有鱼最少
需要几颗炸弹。
解题思路:
本题其实是一个最小步数模型,也就是鱼的初始状态到最终状态的变化,由于只有鱼所在格子自己以及上下左右5个地方放炸弹才有效,因此可以枚举炸弹的位置,然后进行状态的改变,最终状态就是鱼的数量变成0,只要状态定义好了,通过BFS即可求得最小步数,每一步对应一个炸弹。
问题的关键是鱼的状态如何定义?
最多10个格子有鱼,每个格子最多3条鱼,因此可以用最多20位二进制整数来表示鱼,每两位二进制表示该位置的鱼的数量,
比如:00110110表示一共四个格子有鱼,第0个格子的鱼的数量是低位10,也就是2条;第1个格子是01,也就是1条。。。
数据结构方面,进行如下定义即可满足所需:
vector<pii> fishpos; //所有有鱼的格子坐标
map<pii, int> posidx; //有鱼的格子坐标对应的编号
set<pii> bomb; //所有可以放炸弹的格子坐标,可去重
再封装两个函数用于对特定格子的鱼的数量进行修改或者查询
//将state第idx * 2起的两位二进制的值改成value,表示设置第idx个有鱼格子的鱼的数量
int setValue(int state, int idx, int value)
{
idx *= 2;
return state & ~(3 << idx) | (value << idx);
}
//查询state第idx * 2起的两位二进制的值,表示查询第idx有鱼格子的鱼的数量
int getValue(int state, int idx)
{
idx *= 2;
return state >> idx & 3;
}
100分代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 1005;
int dx[5] = {0, -1, 1, 0, 0};
int dy[5] = {0, 0, 0, 1, -1};
vector<pii> fishpos; //所有有鱼的格子坐标
map<pii, int> posidx; //有鱼的格子坐标对应的编号
set<pii> bomb; //所有可以放炸弹的格子坐标,可去重
int state; //初始状态
int dist[1 << 20];
int n, m, k;
//将state第idx * 2起的两位二进制的值改成value,表示设置第idx个有鱼格子的鱼的数量
int setValue(int state, int idx, int value)
{
idx *= 2;
return state & ~(3 << idx) | (value << idx);
}
//查询state第idx * 2起的两位二进制的值,表示查询第idx有鱼格子的鱼的数量
int getValue(int state, int idx)
{
idx *= 2;
return state >> idx & 3;
}
int main()
{
cin >> n >> m >> k;
for(int i = 0; i < k; i++)
{
int x, y, z;
cin >> x >> y >> z;
fishpos.push_back({x, y});
posidx[{x, y}] = i;
state = setValue(state, i, z);
for(int j = 0; j < 5; j++)
{
int nx = x + dx[j], ny = y + dy[j];
bomb.insert({nx, ny}); //添加炸弹可能的位置
}
}
queue<int> q;
q.push(state);
memset(dist, 0x3f, sizeof(dist));
dist[state] = 0;
while(q.size())
{
int st = q.front(); q.pop();
if(st == 0) break; //终止状态
for(auto b : bomb) //枚举所有炸弹可能的位置
{
int tmp = st; //复制状态
for(int i = 0; i < 5; i++) //枚举炸弹覆盖的范围
{
int nx = b.first + dx[i], ny = b.second + dy[i];
if(posidx.count({nx, ny})) //炸弹覆盖的格子中有鱼
{
int id = posidx[{nx, ny}];
int value = getValue(tmp, id);
if(value) tmp = setValue(tmp, id, value - 1); //鱼的数量大于0则减1
}
}
if(dist[tmp] == 0x3f3f3f3f)
{
dist[tmp] = dist[st] + 1;
q.push(tmp);
}
}
}
cout << dist[0];
return 0;
}
浙公网安备 33010602011771号