Loading

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

posted @ 2021-06-10 11:03  Last_order  阅读(71)  评论(0)    收藏  举报