It‘s not a Bug, it‘s a Feature

链接:

https://vjudge.net/problem/UVA-658#author=shiyifan
https://zoj.pintia.cn/problem-sets/91827364500/problems/91827364691

题面:

It‘s not a Bug, it‘s a Feature_第1张图片

(大佬学长费心思搞的中文题面,太感动了呜呜呜)

分析:

起始一个全是bug的串,记为 111,然后给三个补丁

从第一个看

  第一串 000 :

    0代表无要求,这一位有没有bug都行,也就是说,这个补丁可以应用在所有的状态

  第二串 00-0代表跟应用补丁前相同,-代表这一位无论原来有没有bug都重新置为 无bug 

第二个:

  第一串 00-:

    前两位无要求,后一位必须为 无bug

  第二串 0-+:

    第一位和原来一样,后两位分别置为 无bug 和 有bug

所以,目标就是把起始 111(全bug) 的串应用一些补丁使其变为 000。

要求求出最短的耗时(路径),因为每个补丁耗时不同,可以看做 正权最短路问题,这里我用 dijkstra 来写。

正常来说 dijkstra 是从起始节点经过一条条边到达最终节点,那么对于这个问题,当前串的状态便是一个个的节点,每个补丁看作一条条边。

具体怎么写呢?得解决以下几个问题:

  1. 当前节点的状态如何存储?使用字符串嘛?

可以用二进制来代表,111 代表全为bug,其对应的十进制为 7,同时也方便后续进行状态转移操作。

  1. 既然用二进制表示,那如何得到起点?

pow(2, n) - 1即可得到代表 n 位全 1 的十进制数字,最大为 2^20。

  1. 进行搜索时,是像模板一样遍历每个点还是遍历能走的每条边?

都可以,我这里选择遍历每一个边,同时判断这个边能不能走,毕竟只有100条边

  1. 怎样判断当前这个边能不能走?

假设边为 0± >> 0–
当前状态为 110,需要判断每一位是否符合要求
利用C++的 左移右移运算符即可
遍历补丁的状态每一位,若为 + / - 则判断当前状态对应那一位是否符合要求

  1. 怎么取当前状态的每一位呢?

设长度为 n, 取状态第一位为 110 >> n - 1(2) 为 1
取第二位 110 >> n - 2(1) 为 11
取第三位 110 >> n - 3(0) 为 110

  1. 你这么取也不是单独一位啊,怎么判断是否符合要求呢?

当我们取到补丁第二位的 + 时
只要将对应状态的那一位 & 1,若为 1 则说明符合, 为 0 则不符合
对状态 右移1位后,为 11, 此时 & 1(01) ,不论状态之前的位是什么
结果只与最后一位有关
为 - 时也一样It‘s not a Bug, it‘s a Feature_第2张图片

  1. 知道当前边能不能走之后,如何走过去呢?

也就是说,把状态对应的位置为 0/1
如何置任何一位为 1呢:(或)| 上 1 >> n 即可
It‘s not a Bug, it‘s a Feature_第3张图片
置 0 时:& 上 (取反)~(1 >> n) 即可
It‘s not a Bug, it‘s a Feature_第4张图片

代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef pair<int, int> PII;
const int N = 2e7, M = 300;
int n, m;
bool st[N];
struct Patch // 补丁
{
    int id;
    string prev, curr;
}patches[M];

bool check(int ver, int i) // 判断这个边能不能走
{
    string s = patches[i].prev;
    for (int i = n - 1, j = 0; i >= 0 && j < n; i--, j++)
    {
        int temp = ver >> i;
        if (s[j] == '+' && !(temp & 1)) return false;
        else if (s[j] == '-' && (temp & 1)) return false;
    }
    return true;
}

int mkpatch(int ver, int i) // 得到走过去后的状态
{
    string s = patches[i].curr;
    int temp = ver;
    for (int i = n - 1, j = 0; i >= 0 && j < n; i--, j++)
    {
        if (s[j] == '+') temp = temp | (1 << i);
        else if (s[j] == '-') temp = temp & (~(1 << i));
    }
    return temp;
}

int dijkstra()
{
    memset(st, false, sizeof st);
    priority_queue<PII, vector<PII>, greater<PII> >q; // 小根堆 优化
    q.push({ 0,pow(2,n) - 1 }); // 从起点开始
    while (!q.empty())
    {
        PII t = q.top();
        q.pop();
        int ver = t.second, cost = t.first;
        if (ver == 0) return cost; // 若此时状态为 0, 则说明已经完成
        if (st[ver]) continue;
        st[ver] = true;
        for (int i = 0; i < m; i++)
        {
            if (check(ver, i)) // 是否有边
            {
                int temp = mkpatch(ver, i);
                if (!st[temp])
                    q.push({ cost + patches[i].id, temp});
            }
        }
    }
    return -1;
}


int main()
{
    ios::sync_with_stdio(false); cin.tie(0);
    int cnt = 0;
    while (cin >> n >> m && n + m)
    {

        for (int i = 0; i < m; i++)
            cin >> patches[i].id >> patches[i].prev >> patches[i].curr; 
        int res = dijkstra();
        cout << "Product " << ++cnt << "\n";
        if (res == -1) cout << "Bugs cannot be fixed.\n";
        else cout << "Fastest sequence takes " << res << " seconds.\n";
        cout << "\n";
    }
    return 0;
}

你可能感兴趣的:(ACM入门题目,算法,c++,图搜索算法)