版权声明:本人QQ 249712949 欢迎交流算法(请备注)。 https://blog.csdn.net/coord_/article/details/88785205
最短路。
这道题题意比较烦,其实就是成语接龙。给你一个成语的列表(字典)(每个成语用十六进制字符串表示),规定字典的第一个成语和最后一个成语是你完成成语接龙的起点和终点,然后你在字典里面选成语接龙中间的成语,A->B
当且仅当A
的后四个字符等于B
的前四个字符。然后每个成语给你的时候还额外给一个数,表示这个成语到其所有的后驱成语(若有)的边的权值(可以看出最后那个成语的这个数没用)。问你起点到终点最小值。
其实就是把成语看成点,这个字典一行一行给你点的信息,但是点之间的边的关系是隐藏的,那我就建两个数组存储前缀和后缀,输入当前点时,拿这个点的前缀后缀和前面那些点的后缀数组前缀数组比较然后建边就ok了(想一想为什么可以边输入边查找?为什么不用考虑后面输入的点和这个点的关系?2333),这样就完成了建图。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <map>
using namespace std;
const int INF = 1e9;
const int MAXN = 1002;
int N;
int d[MAXN];
struct Edge
{
int to, w;
};
vector<Edge> v[MAXN];
vector<string> first;
vector<string> last;
int dis[MAXN];
bool vis[MAXN];
void init()
{
first.clear();
last.clear();
for (int i = 0; i < N; i++) v[i].clear();
}
void dijkstra(int s)
{
fill(dis, dis + N, INF);
memset(vis, 0, sizeof vis);
dis[s] = 0;
for (int i = 0; i < N; i++)
{
int mind = INF, u = -1;
for (int j = 0; j < N; j++)
{
if (!vis[j] && dis[j] < mind)
{
mind = dis[j];
u = j;
}
}
if (u == -1) break;
vis[u] = 1;
for (int j = 0; j < v[u].size(); j++)
{
int t = v[u][j].to;
int w = v[u][j].w;
if (!vis[t] && dis[u] + w < dis[t])
{
dis[t] = dis[u] + w;
}
}
}
}
int main()
{
string str, f, l;
for (; ~scanf("%d", &N);)
{
if (!N) break;
init();
for (int i = 0; i < N; i++)
{
cin >> d[i] >> str;
f = str.substr(0, 4);
l = str.substr(str.size() - 4, 4);
for (int j = 0; j < last.size(); j++)
{
if (last[j] == f)
v[j].push_back(Edge{ i,d[j] });
}
for (int j = 0; j < first.size(); j++)
{
if (first[j] == l)
v[i].push_back(Edge{ j,d[i] });
}
last.push_back(l); // 这两行放到查找后面是因为怕出现自连接边(这个成语本身首尾相等),但其实出现了也无影响
first.push_back(f);
}
dijkstra(0);
if (dis[N - 1] == INF)
printf("-1\n");
else printf("%d\n", dis[N - 1]);
}
return 0;
}