https://vjudge.net/problem/UVA-658#author=shiyifan
https://zoj.pintia.cn/problem-sets/91827364500/problems/91827364691
(大佬学长费心思搞的中文题面,太感动了呜呜呜)
分析:起始一个全是bug的串,记为 111,然后给三个补丁
从第一个看
第一串 000 :
0代表无要求,这一位有没有bug都行,也就是说,这个补丁可以应用在所有的状态
第二串 00-:
0代表跟应用补丁前相同,-代表这一位无论原来有没有bug都重新置为 无bug
第二个:
第一串 00-:
前两位无要求,后一位必须为 无bug
第二串 0-+:
第一位和原来一样,后两位分别置为 无bug 和 有bug
所以,目标就是把起始 111(全bug) 的串应用一些补丁使其变为 000。
要求求出最短的耗时(路径),因为每个补丁耗时不同,可以看做 正权最短路问题,这里我用 dijkstra 来写。
正常来说 dijkstra 是从起始节点经过一条条边到达最终节点,那么对于这个问题,当前串的状态便是一个个的节点,每个补丁看作一条条边。
具体怎么写呢?得解决以下几个问题:
- 当前节点的状态如何存储?使用字符串嘛?
可以用二进制来代表,111 代表全为bug,其对应的十进制为 7,同时也方便后续进行状态转移 *** 作。
- 既然用二进制表示,那如何得到起点?
pow(2, n) - 1
即可得到代表 n 位全 1 的十进制数字,最大为 2^20。
- 进行搜索时,是像模板一样遍历每个点还是遍历能走的每条边?
都可以,我这里选择遍历每一个边,同时判断这个边能不能走,毕竟只有100条边
- 怎样判断当前这个边能不能走?
假设边为 0± >> 0–
当前状态为 110,需要判断每一位是否符合要求
利用C++的 左移右移运算符即可
遍历补丁的状态每一位,若为 + / - 则判断当前状态对应那一位是否符合要求
- 怎么取当前状态的每一位呢?
设长度为 n, 取状态第一位为 110 >> n - 1(2) 为 1
取第二位 110 >> n - 2(1) 为 11
取第三位 110 >> n - 3(0) 为 110
- 你这么取也不是单独一位啊,怎么判断是否符合要求呢?
当我们取到补丁第二位的 + 时
只要将对应状态的那一位 & 1,若为 1 则说明符合, 为 0 则不符合
对状态 右移1位后,为 11, 此时 & 1(01) ,不论状态之前的位是什么
结果只与最后一位有关
为 - 时也一样
- 知道当前边能不能走之后,如何走过去呢?
代码:也就是说,把状态对应的位置为 0/1
如何置任何一位为 1呢:(或)| 上 1 >> n 即可
置 0 时:& 上 (取反)~(1 >> n) 即可
#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;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)