题目大意:
成语接龙游戏,给定一些成语,该组成语中第1个和最后一个必须是给定的两个成语。在这组成语中,前一个成语的最后一个汉字必须和后一个成语的第一个汉字相同。在游戏过程中,Tom有一本字典,他必须从字典中选用成语。字典中每个成语都有一个权值T,表示选用这个成语后,Tom需要花时间T才能找到下一个合适的成语。你的任务是编写程序,给定字典,计算Tom至少需要花多长时间才能找到一个满足条件的成语组。注意,字典中第1个和最后一个成语为游戏中给定的起始和目标成语。输入文件最后一行为N = 0,代表输入结束。
分析:
假设用图中的顶点代表字典中的每个成语,如果第i个成语的最后一个汉字跟第j个成语的第一个汉字相同,则画一条有向边,由顶点i 指向顶点j,权值为题目中所提到的时间T:选用第i个成语后,Tom需要花时间T才能找到下一个合适的成语。这样,样例输入中两个测试数据所构造的有向网如下图(a)和(b)所示。
构造好有向网后,问题就转化成求一条从顶点0到顶点N-1的一条最短路径,如果从顶点0到顶点N-1没有路径,则输出-1。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 1010;
const int INF = 10000010;
struct Idiom
{
char front[5],back[5];//每个成语的前一个字和后一个字
int T;//查找单词所花费的时间
};
Idiom dic[MAXN];//存放每个成语的属性
int S[MAXN];
int Edge[MAXN][MAXN];//储存图
int dist[MAXN];//记录从原点到每个顶点的最短路径
int n;
void Dijkstra(int v0)
{
int i, j;
for (i = 0; i < n; ++i)
{
dist[i] = Edge[v0][i];
S[i] = 0;
}
S[v0] = 1, dist[v0] = 0;
for (i = 1; i < n; ++i)
{
int MIN = INF, v = v0;
for (j = 0; j < n; ++j)
{
if(!S[j] && MIN > dist[j])
{
MIN = dist[j];
v = j;
}
}
S[v] = 1;
for (j = 0; j < n; ++j)
{
if(!S[j] && Edge[v][j] < INF && dist[j] > dist[v] + Edge[v][j])
dist[j] = dist[v] + Edge[v][j];
}
}
}
int main()
{
int i, j, k;
while (cin>>n)
{
if(n == 0)
break;
for (i = 0; i < n; ++i)
{
char s[50];
cin>>dic[i].T>>s;
int len = strlen(s);
for (j = 0, k = len-1; j < 4; ++j, --k)//记录第i个成语的第一个字和最后一个字
{
dic[i].front[j] = s[j];
dic[i].back[3-j] = s[k];
}
dic[i].back[4] = dic[i].front[4] = '\0';
}
for (i = 0; i < n; ++i)//建图
{
for (j = 0; j < n; ++j)
{
Edge[i][j] = INF;
if(i == j)
continue;
if(strcmp( dic[i].back, dic[j].front ) == 0)//说明第i个成语的最后一个字与第j个成语的第一个字相同,则,连一条从i到j的边
Edge[i][j] = dic[i].T;//权值为花费的时间
}
}
Dijkstra( 0 );
if(dist[n-1] < INF)
cout<<dist[n-1]<<endl;
else
cout<<"-1"<<endl;
}
return 0;
}