题意:
初始串经过一系列操作可以变换成目标串,每一次操作需要耗费一定的能量,求最小的能量。
思路:
1. 这一题同样采取 BFS 但是求的结果并不是最短路径,而是最小的耗能。其实也就是变换下思路,每次深搜之后把耗能最小的先出队列即可;
2. 这样的话可以把题目转换成优先队列的 dijstra 算法, 从源点到目标点的距离 -> 这里的点的变换即是状态的变换;
3. 题目中有个小 trick 使得一般的优先队列 BFS 算法无效,考虑如下 case:
FFFF 10 , FFNN 1 , NNFF 1 , 0000 -> 1111 显然最小功耗是 2 而非 10,所以采用 dijstra 算法是合适的。
4. 由于题目中 L <= 20,所以在本题采用位运算会使过程变得十分优美,最终代码跑到了 141ms ;
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
struct ST {
int state, cost;
ST(int _state, int _cost) : state(_state), cost(_cost) {}
bool operator < (const ST& other) const { return cost > other.cost; }
};
struct OP {
int op_and, op_or, op_xor;
int cost;
} op[40];
const int INFS = 0x3fffffff;
int L, N, M, dis[1<<20];
bool vis[1<<20];
int bfs(int s, int t) {
priority_queue<ST> Q;
Q.push(ST(s, 0));
for (int i = 0; i < 1<<L; i++)
dis[i] = INFS, vis[i] = false;
dis[s] = 0;
while (!Q.empty()) {
ST u = Q.top();
Q.pop();
if (u.state == t)
return u.cost;
vis[s] = true;
for (int i = 0; i < N; i++) {
int v = u.state;
v &= op[i].op_and;
v |= op[i].op_or;
v ^= op[i].op_xor;
if (!vis[v] && dis[v] > u.cost + op[i].cost) {
dis[v] = u.cost + op[i].cost;
Q.push(ST(v, dis[v]));
}
}
}
return -1;
}
int main() {
int cases;
scanf("%d", &cases);
while (cases--) {
scanf("%d%d%d", &L, &N, &M);
for (int i = 0; i < N; i++) {
char s[30];
scanf("%s%d", s, &op[i].cost);
op[i].op_and = op[i].op_or = op[i].op_xor = 0;
for (int j = 0; s[j]; j++) {
op[i].op_and <<= 1;
op[i].op_or <<= 1;
op[i].op_xor <<= 1;
if (s[j] == 'N')
op[i].op_or |= 0;
else if (s[j] == 'C')
op[i].op_and |= 1;
else if (s[j] == 'F')
op[i].op_xor |= 1;
else if (s[j] == 'S')
op[i].op_or |= 1;
}
op[i].op_and = ~op[i].op_and;
}
for (int i = 0; i < M; i++) {
char src[30], dst[30];
int u = 0, v = 0;
scanf("%s%s", src, dst);
for (int j = 0; src[j]; j++)
u <<= 1, u |= (src[j] - '0');
for (int j = 0; dst[j]; j++)
v <<= 1, v |= (dst[j] - '0');
int ans = bfs(u, v);
if (ans != -1)
printf("%d", ans);
else
printf("NP");
printf("%c", i == M-1 ? '\n' : ' ');
}
}
return 0;
}