CF33B String Problem
废话
重铸dijkstra荣光,我辈义不容辞!
说实话我刚看到这道题还以为是dp。
分析 & 思路
一道比较裸的最短路 + 字符串,下文中用\(dis(a,b)\)表示从字母 \(a\) 到字母 \(b\) 的最短路。
题意已经很明确了,就是问我们把 \(A\) 串和 \(B\) 串变成同样的字符串,所需的最小代价以及最后相同的串。考虑遍历一遍 \(A\) 串和 \(B\) 串,求 \(\sum^{len(a)}_{i = 1} Min\{dis(a_i ,k) + dis(b_i, k)\}\)( \(k\) 为 \(a \sim z\) 的一个字母)。
即找出一个字母 \(k\) ,使得\(A_i\) 到 \(k\) 的最短路加上 \(B_i\) 到 \(k\) 的最短路最小。
显然我们并不能每遍历到一个字母就做一次 dijk,但是想到点数非常少,最多只有 26 个,所以考虑遍历 26 个字母,对于每个字母做一次 dijk,然后把这个字母到每个字母的最短路存到一个dis[start][end]数组里面。这样就可以做到 \(O(n(n + m)\log{n})\;(n \le 26)\) 预处理, \(O(26 \times len(a))\) 求解,还是挺快的。
建图时,给每一个字母一个编号,从 0 开始。当然dis数组的下标也是字母的编号。
代码
#include <cstdio>
#include <cstring>
#include <queue>
#define pair std::pair
#define make_pair std::make_pair
#define inf 10000000
#define maxn 100005
struct Edge{
int start, end, val, nexty;
}edge[maxn];
char u, v, ans[maxn], a[maxn], b[maxn];
int m, w, cnt_edge, head[maxn], dis[30][30];
int sum, minn, a1, b1, pay;
bool vis[30];
inline void add_edge(int u, int v, int w){
edge[++cnt_edge] = (Edge){u, v, w, head[u]};
head[u] = cnt_edge;
}
inline void dijk(int s){
std::memset(vis, 0, sizeof vis);
for(register int i = 0; i < 26; i++) dis[s][i] = inf;
dis[s][s] = 0;
std::priority_queue < pair < int , int > > q;
q.push(make_pair(0, s));
while(!q.empty()){
int u = q.top().second;
q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; i; i = edge[i].nexty){
int v = edge[i].end;
if(dis[s][v] > dis[s][u] + edge[i].val){
dis[s][v] = dis[s][u] + edge[i].val;
q.push(make_pair(-dis[s][v], v));
}
}
}
}
int main(){
scanf("%s\n%s", a + 1, b + 1);
int lena = strlen(a + 1), lenb = strlen(b + 1);
if(lena != lenb){
puts("-1");
return 0;
}
scanf("%d", &m);
for(int i = 1; i <= m; i++){
scanf(" %c %c %d", &u, &v, &w);
add_edge((u - 'a'), (v - 'a'), w);
}
for(register int i = 0; i < 26; i++){
dijk(i);
}
for(int i = 1; i <= lena; i++){
if(a[i] == b[i]){
ans[i] = a[i];
continue;
}
minn = inf;
a1 = (a[i] - 'a'), b1 = (b[i] - 'a');
for(int j = 0; j < 26; j++){
if(dis[a1][j] == inf || dis[b1][j] == inf) continue;
pay = dis[a1][j] + dis[b1][j];
if(pay < minn) ans[i] = j + 'a', minn = pay;
}
if(minn == inf){puts("-1"); return 0;}
sum += minn;
}
printf("%d\n%s", sum, ans + 1);
return 0;
}
被写 floyd 的大佬暴踩了 orz

浙公网安备 33010602011771号