F - Construct a Palindrome (AtCoder Beginner Contest 197)
题目来源
https://atcoder.jp/contests/abc197/tasks/abc197_f
题意分析
给一张图,每条边上面有一个字母,试着找出一条从起点出发到终点的一条最短路,使得按顺序每条边上的字母相连接是一个回文串,若不存在则输出 -1。
思路分析
思维题。数据范围是1000量级,第一眼以为是dp,第二眼是双端bfs,卡了好久。
解法是在我们建好这条路之后,我们往队列里面加入所有的长度为1的边以及长度为0的边(即以两个相同的点为两边的边),每次扩展的时候,原本长度为1的边往两边延伸扩展找边,直到两边找到字母相同的边时候,更新距离最小值,同时状态入队。
于是可以发现的是,原本长度为1的边最后扩展出来的是奇数回文,长度为0的边最后扩展出来是偶数回文。
code
#include <bits/stdc++.h> #define ll long long #define INF 0x3f3f3f3f using namespace std; const int maxn = 1e3 + 7; int head[maxn], nxt[maxn << 2], ver[maxn << 2], tot; char c[maxn << 2]; int dis[maxn][maxn]; void adde(int u, int v, char ch){ ++tot; ver[tot] = v; c[tot] = ch; nxt[tot] = head[u]; head[u] = tot; } int main(){ int n, m; scanf("%d%d", &n, &m); for (int i=1; i<=m; i++){ int u, v; char ch; scanf("%d%d %c", &u, &v, &ch); adde(u, v, ch); adde(v, u, ch); } queue <pair <int, int> > q; for (int i=1; i<=n; i++){ for (int j=1; j<=n; j++) dis[i][j] = INF; dis[i][i] = 0; } for (int i=1; i<=n; i++) q.push({i, i}); for (int i=1; i<=n; i++){ for (int j=head[i]; j; j=nxt[j]){ int v = ver[j]; if (v <= i) continue; dis[i][v] = dis[v][i] = 1; q.push({i, v}); } } int ans = 0; while (!q.empty()){ int h1 = q.front().first, h2 = q.front().second; q.pop(); for (int i=head[h1]; i; i=nxt[i]){ int v1 = ver[i]; for (int j=head[h2]; j; j=nxt[j]){ int v2 = ver[j]; if (c[i] == c[j] && v1 != v2){ if (dis[v1][v2] > dis[h1][h2] + 2){ dis[v1][v2] = dis[v2][v1] = dis[h1][h2] + 2; q.push({v1, v2}); } } } } } if (dis[1][n] == INF) printf("-1\n"); else printf("%d\n", dis[1][n]); return 0; } /* 3 3 1 2 a 2 3 a 1 3 a */