洛谷题单指南-状态压缩动态规划-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;
}

 

posted @ 2025-09-06 23:05  hackerchef  阅读(8)  评论(0)    收藏  举报