/**
题目来源:https://nanti.jisuanke.com/t/16959
题目翻译:给出M条道路关系,每条道路关系的格式起始地点字符串,终止地点字符串,
距离。该道路是双向的,然后让求从Dalian 到 Xian 必须经过上海的最短路径.途中每个
城市只能经过一次。
解题思路:之前比赛的时候没有学习最小费用最大流,做的时候一直错了。现在再看它和
POJ 2135是很类似的,POJ 2135是给一个图,从1到N再从N到1回去不允许经过同一段路。
用流量为2的最小费用最大流解决的,而这个说的是一个城市只能去一次,暗含了该节点的
流量为1,则它是属于节点流量有限制的最小费用最大流,因此我们把一个点拆成两个点,
其流量是1,以上海为超级汇点,注意上海拆点其容量应该是2,然后建立超级源点,分别与大连
和西安相连,最大流是2,则答案存在,否则无答案,不能找到满足题目要求的路。
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <map>
using namespace std;
const int maxn = 100000;
const int inf = 0x7f7f7f7f;
struct Edge{
int from; ///起点
int to; ///终点
int cap; ///容量
int cost; ///花费
int nex; ///以from为起点的下条边的所在位置
}edge[maxn];
int head[maxn],cnt; ///head是头节点,cnt是边的数目
int M,S,T,id; ///M是图中边数,S超级源点,T超级汇点,id用来给地点编号
int maxFlow,minCostMaxFlow,minFlow;
///maxFlow最大流,minFlow一条增广路中的最小流,minCostMaxFlow是最小费用
int vis[maxn],dist[maxn],pre[maxn];
///vis标记数组,dist存放S到各个节点的距离,pre存放点v所在边的编号
map<string,int>m; ///通过映射对节点编号
///加边函数
void addEdge(int u,int v,int cap,int cost) {
///正向边
edge[cnt].from = u;
edge[cnt].to = v;
edge[cnt].cap = cap;
edge[cnt].cost = cost;
edge[cnt].nex = head[u];
head[u] = cnt++;
///添加反向边
edge[cnt].from = v;
edge[cnt].to = u;
edge[cnt].cap = 0;
edge[cnt].cost = -cost;
edge[cnt].nex = head[v];
head[v] = cnt++;
}
///进行初始化的函数
void init() {
m.clear(); ///清空集合
memset(head,-1,sizeof(head)); ///头结点初始化为-1
cnt = 0; ///边数为0
S = 0; ///默认超级原地啊编号为0
id = 1; ///地点编号从1开始
}
///spfa找一条费用最小的增广路
bool spfa() {
queue<int>qu;
memset(pre,-1,sizeof(pre));
for(int i = 0; i < id; i++) {
dist[i] = inf;
vis[i] = 0;
}
vis[S] = 1;
dist[S] = 0;
qu.push(S);
while(!qu.empty()) {
int u = qu.front();
qu.pop();
vis[u] = 0;
for(int i = head[u]; i != -1; i = edge[i].nex) {
int v = edge[i].to;
if(edge[i].cap>0 && dist[u]+edge[i].cost<dist[v]) {
dist[v] = dist[u]+edge[i].cost;
pre[v] = i;
if(vis[v] == 0) {
vis[v] = 1;
qu.push(v);
}
}
}
}
if(dist[T]<inf) return true;
else return false;
}
///最小费用最大流求解
bool min_cost_max_flow() {
minCostMaxFlow = 0; ///最小费用最大流刚开始赋值为0
maxFlow = 0;
while(spfa()) {
minFlow = inf; ///一路上的最大流量初始化我inf
for(int i = pre[T]; i != -1; i = pre[edge[i].from]) {
minFlow = min(minFlow,edge[i].cap);
}
for(int i = pre[T]; i != -1; i = pre[edge[i].from]) {
edge[i].cap -= minFlow;
edge[i^1].cap += minFlow;
}
minCostMaxFlow += dist[T]*minFlow;
maxFlow += minFlow;
if(maxFlow == 2) return true;
}
return false;
}
int main() {
int Case;
scanf("%d",&Case); ///册数数据组数
while(Case--) {
init();
scanf("%d",&M); ///M条边
char src[100],des[100];
int len,u,v;
for(int i = 1; i <= M; i++) {
scanf("%s%s%d",src,des,&len);
if(m[src] == 0){
m[src] = id++;
id++;
///上海拆点的容量得是2
if(strcmp(src,"Shanghai")==0) {
addEdge(m[src],m[src]+1,2,0);
} else {
addEdge(m[src],m[src]+1,1,0);
}
}
u = m[src];
if(m[des] == 0) {
m[des] = id++; ///一个点拆成两点个点,占据两个编号
id++;
if(strcmp(des,"Shanghai")==0) {
addEdge(m[des],m[des]+1,2,0);
} else {
addEdge(m[des],m[des]+1,1,0);
}
}
v = m[des];
///城市间的道路是双向的
addEdge(u+1,v,inf,len); ///从源点拆出的点往目的点引边
addEdge(v+1,u,inf,len);
}
addEdge(S,m["Dalian"],1,0);
addEdge(S,m["Xian"],1,0);
T = m["Shanghai"]+1;
if(min_cost_max_flow())
printf("%d\n",minCostMaxFlow);
else
printf("-1\n");
}
return 0;
}