图论专题III
[vijos 美食节]最小费用流
From:https://www.vijos.org/p/1726
Solution:
假设厨师i做菜品j做n次, 那么第k次做时对答案的贡献就是(n-k+1)*t[j][i]。我们把每个厨师进行拆点, 建图用费用流即可算出答案。但是这题图较大, 需要动态加边。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <math.h> #include <queue> #include <stack> #include <functional> using namespace std; #define N 45 #define M 101 #define oo 0x6fffffff #define nil (-1) bool inQ[N*M]; struct {int c,w,to,next;} nod[20*N*M]; int adj[N*M], d[N*M], fa[N*M], idx[N*M], last[M], t[N][M], p[N], cnt[M], now, S, T; void Init(int n){ now = 0; S = 0; T = n+1; for (int i = 0; i <= T; ++i) adj[i] = nil; } int NewNode(int u, int w, int c){ nod[now].to = u; nod[now].w = w; nod[now].c = c; nod[now].next = nil; return now++; } void AddEdge(int u, int v, int w, int c){ int u1 = NewNode(v, w, c); int v1 = NewNode(u, -w, 0); nod[u1].next = adj[u]; adj[u] = u1; nod[v1].next = adj[v]; adj[v] = v1; } bool Spfa(int n){ for (int i = 0; i <= n; ++i) d[i] = oo, fa[i] = nil, inQ[i] = false; queue<int> Q; d[S] = 0; Q.push(S); inQ[S] = true; while ( !Q.empty() ){ int u = Q.front(); Q.pop(); inQ[u] = false; for (int i = adj[u]; i != nil; i = nod[i].next){ int v = nod[i].to; if ( nod[i].c > 0 && d[v] > d[u] + nod[i].w ){ d[v] = d[u] + nod[i].w; fa[v] = u; idx[v] = i; if ( !inQ[v] ) inQ[v] = true, Q.push(v); } } } return d[T] != oo; } int Argument(){ int cf = oo; for (int i = T; i != S; i = fa[i]) cf = min(cf, nod[idx[i]].c); for (int i = T; i != S; i = fa[i]){ nod[idx[i]].c -= cf; nod[idx[i]^1].c += cf; } return d[T]; } int main() { int n, m; while ( scanf("%d%d", &n, &m) != EOF ){ int sp = 0; for (int i = 1; i <= n; ++i){ scanf("%d", p+i); sp += p[i]; } Init(n+m+sp); for (int i = 1; i <= n; ++i){ AddEdge(S, i, 0, p[i]); for (int j = 1; j <= m; ++j){ scanf("%d", &t[i][j]); AddEdge(i, n+j, t[i][j], 1); } } for (int j = 1; j <= m; ++j){ AddEdge(n+j, T, 0, 1); last[j] = now-2; cnt[j] = 1; } int ans = 0; for (int k = 1; k <= sp && Spfa(n+m+sp+1); ++k){ ans += Argument(); int j = 1; for ( ; j <= m && nod[last[j]].c; ++j); ++cnt[j]; for (int i = 1; i <= n; ++i){ AddEdge(i, n+m+k, cnt[j]*t[i][j], 1); } AddEdge(n+m+k, T, 0, 1); last[j] = now-2; } printf("%d\n", ans); } return 0; }
[vijos 丛林探险]最短路
From:https://www.vijos.org/p/1082
Solution:
这道题有两个纬度, 体力和时间。丢弃任何一个纬度, 就变成简单的最短路径了。容易发现, 体力最短的不一定时间最短, 时间最短的不一定体力最短, 这提示我们需要记录到某个顶点u时的体力和对应的时间, 然后枚举。不妨记dp[i][0][0]是从源点到i时最少体力对应的(体力,时间)花费, dp[i][0][1]是从源点到i时最小时间对应的(体力,时间)花费, dp[i][1][0]是从汇点到i时最小体力对应的(体力,时间)花费, dp[i][1][1]是从汇点到i时最小时间对应的(体力,时间)花费。那么就会出现四种情况:
dp[i][0][0]+dp[i][1][0]: 从源到i的最小体力和从i到汇点的最小体力下的花费
dp[i][0][0]+dp[i][1][1]: 从源到i的最小体力和从i到汇点的最小时间下的花费
dp[i][0][1]+dp[i][1][0]: 从源到i的最小时间和从i到汇点的最小体力下的花费
dp[i][0][1]+dp[i][1][1]: 从源到i的最小时间和从i到汇点的最小时间下的花费
因此, 枚举点i, 即可算出从源到汇的满足体力限制的最小时间。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <math.h> #include <queue> #include <stack> #include <functional> using namespace std; #define N 5005 #define M 40005 #define oo 0x6fffffff #define nil (-1) bool inQ[N]; struct {int c,w,to,next;} nod[M*2]; int adj[N], d[N], c[N], dp[N][2][2][2], now; void Init(int n){ now = 0; for (int i = 0; i <= n; ++i) adj[i] = nil; } int NewNode(int v, int c, int w){ nod[now].to = v; nod[now].c = c; nod[now].w = w; nod[now].next = nil; return now++; } void AddEdge(int u, int v, int c, int d){ int u1 = NewNode(v, c, d); nod[u1].next = adj[u]; adj[u] = u1; } void Spfa_w(int S, int n, bool fromT){ for (int i = 0; i <= n; ++i) d[i] = oo, c[i] = 0, inQ[i] = false; queue<int> Q; d[S] = 0; inQ[S] = true; Q.push(S); dp[S][fromT][0][1] = dp[S][fromT][1][1] = 0; while ( !Q.empty() ){ int u = Q.front(); Q.pop(); inQ[u] = false; for (int i = adj[u]; i != nil; i = nod[i].next){ int v = nod[i].to; if ( d[v] > d[u] + nod[i].w ){ d[v] = d[u] + nod[i].w; c[v] = c[u] + nod[i].c; dp[v][fromT][0][1] = c[v]; dp[v][fromT][1][1] = d[v]; if ( !inQ[v] ) inQ[v] = true, Q.push(v); } } } } void Spfa_c(int S, int n, bool fromT){ for (int i = 0; i <= n; ++i) d[i] = 0, c[i] = oo, inQ[i] = false; queue<int> Q; c[S] = 0; inQ[S] = true; Q.push(S); dp[S][fromT][0][0] = dp[S][fromT][1][0] = 0; while ( !Q.empty() ){ int u = Q.front(); Q.pop(); inQ[u] = false; for (int i = adj[u]; i != nil; i = nod[i].next){ int v = nod[i].to; if ( c[v] > c[u] + nod[i].c ){ c[v] = c[u] + nod[i].c; d[v] = d[u] + nod[i].w; dp[v][fromT][0][0] = c[v]; dp[v][fromT][1][0] = d[v]; if ( !inQ[v] ) inQ[v] = true, Q.push(v); } } } } int Run(int S, int T, int k, int n){ Spfa_c(S, n, false); if ( dp[T][0][0][0] > k ) return -1; Spfa_w(S, n, false); Spfa_c(T, n, true); Spfa_w(T, n, true); int ans = oo; for (int i = 1; i <= n; ++i){ int t1 = dp[i][0][0][0] + dp[i][1][0][0]; int t2 = dp[i][0][0][0] + dp[i][1][0][1]; int t3 = dp[i][0][0][1] + dp[i][1][0][0]; int t4 = dp[i][0][0][1] + dp[i][1][0][1]; if ( t1 <= k ) ans = min(ans, dp[i][0][1][0]+dp[i][1][1][0]); if ( t2 <= k ) ans = min(ans, dp[i][0][1][0]+dp[i][1][1][1]); if ( t3 <= k ) ans = min(ans, dp[i][0][1][1]+dp[i][1][1][0]); if ( t4 <= k ) ans = min(ans, dp[i][0][1][1]+dp[i][1][1][1]); } return ans; } int main() { int n,m; while ( scanf("%d%d", &n, &m) != EOF ){ Init(n); for (int i = 0; i < m; ++i){ int a,b,c,d; scanf("%d%d%d%d", &a, &b, &c, &d); AddEdge(a, b, c, d); AddEdge(b, a, c, d); } int s, t, k; scanf("%d%d%d", &s, &t, &k); printf("%d\n", Run(s,t,k,n)); } return 0; }
[vijos Car的旅行路线]最短路
From:https://www.vijos.org/p/1119
Solution:裸的最短路, 需要通过矩形三点求第四点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <math.h> #include <queue> #include <stack> #include <functional> using namespace std; #define N 405 #define M 40005 #define oo 100000000000000 #define nil (-1) struct {double w;int to,next;} nod[N*N]; double d[N]; bool inQ[N]; typedef pair<int,int> ipair; pair<ipair, int> city[N][4]; int adj[N], now, S, T; void Init(int n){ now = 0; S = 0; T = n; for (int i = 0; i <= n; ++i) adj[i] = nil; } int NewNode(int u, double w){ nod[now].to = u; nod[now].w = w; nod[now].next = nil; return now++; } void AddEdge(int u, int v, double w){ int u1 = NewNode(v, w); nod[u1].next = adj[u]; adj[u] = u1; } double Spfa(int S, int T, int n){ for (int i = 0; i <= n; ++i) d[i] = oo, inQ[i] = false; queue<int> Q; d[S] = 0; inQ[S] = true; Q.push(S); while ( !Q.empty() ){ int u = Q.front(); Q.pop(); inQ[u] = false; for (int i = adj[u]; i != nil; i = nod[i].next){ int v = nod[i].to; if ( d[v] > d[u] + nod[i].w && fabs(d[v]-d[u]-nod[i].w) > 1e-10 ){ d[v] = d[u] + nod[i].w; if ( !inQ[v] ) inQ[v] = true, Q.push(v); } } } return d[T]; } bool IsCos90(ipair const& P1, ipair const& P2){ return !(P1.first*P2.first+P1.second*P2.second); } ipair FindPoint(ipair const& p1, ipair const& p2, ipair const& p3){ if ( IsCos90(make_pair(p2.first-p1.first, p2.second-p1.second), make_pair(p3.first-p1.first, p3.second-p1.second)) ){ return make_pair(p2.first+p3.first-p1.first,p2.second+p3.second-p1.second); } if ( IsCos90(make_pair(p1.first-p2.first, p1.second-p2.second), make_pair(p3.first-p2.first, p3.second-p2.second)) ){ return make_pair(p1.first+p3.first-p2.first,p1.second+p3.second-p2.second); } return make_pair(p1.first+p2.first-p3.first,p1.second+p2.second-p3.second); } int main() { int s,t,A,B; while ( scanf("%d%d%d%d", &s, &t, &A, &B) != EOF ){ Init(4*s+1); for (int i = 0; i < s; ++i){ int x1,y1,x2,y2,x3,y3,tt; scanf("%d%d%d%d%d%d%d", &x1, &y1, &x2, &y2, &x3, &y3, &tt); city[i+1][0] = make_pair(make_pair(x1,y1), 4*i+1); city[i+1][1] = make_pair(make_pair(x2,y2), 4*i+2); city[i+1][2] = make_pair(make_pair(x3,y3), 4*i+3); city[i+1][3] = make_pair(FindPoint(ipair(x1,y1), ipair(x2,y2), ipair(x3,y3)), 4*i+4); for (int k1 = 0; k1 < 4; ++k1) for (int k2 = 0; k2 < 4; ++k2){ if ( k1 == k2 ) continue; int x = city[i+1][k1].first.first-city[i+1][k2].first.first; int y = city[i+1][k1].first.second-city[i+1][k2].first.second; double dd = sqrt(x*x+y*y); AddEdge(city[i+1][k1].second, city[i+1][k2].second, dd*tt); } } for (int i = 1; i <= s; ++i) for (int j = 1; j <= s; ++j){ if ( i == j ) continue; for (int k1 = 0; k1 < 4; ++k1) for (int k2 = 0; k2 < 4; ++k2){ int x = city[i][k1].first.first-city[j][k2].first.first; int y = city[i][k1].first.second-city[j][k2].first.second; double dd = sqrt(x*x+y*y); AddEdge(city[i][k1].second, city[j][k2].second, dd*t); } } for (int i = 0; i < 4; ++i){ AddEdge(S, city[A][i].second, 0); } for (int i = 0; i < 4; ++i){ AddEdge(city[B][i].second, T, 0); } printf("%.2lf\n", Spfa(S, T, 4*s+1)); } return 0; }
[vijos 集合位置]最短路
From:https://www.vijos.org/p/1155
Solution:妈蛋, 就因为写了while(scanf("")){...}一直wa, 去掉就过了。 这题不能用d[n][2]表示最短和次短的方式做,因为是无向图,比较难处理。直接删边做即可。找到最短路径, 每次删除路径上的边(记得还原)后求最短路。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <math.h> #include <queue> #include <stack> #include <functional> using namespace std; #define N 205 #define M 40005 #define oo 99999999 #define nil (-1) typedef pair<int,int> ipair; int fa[N]; double d[N], w[N][N]; bool inQ[N]; ipair pt[N], path[M]; void Init(int n){ for (int i = 0; i <= n; ++i) for (int j = 0; j <= n; ++j) w[i][j] = oo; } double Spfa(int s, int t, int n){ for (int i = 0; i <= n; ++i){ d[i] = oo; inQ[i] = false; fa[i] = nil; } queue<int> Q; d[s] = 0; Q.push(s); inQ[s] = true; while ( !Q.empty() ){ int u = Q.front(); Q.pop(); inQ[u] = false; for (int v = 1; v <= n; ++v){ if ( w[u][v] != oo && d[v] > d[u] + w[u][v] ){ fa[v] = u; d[v] = d[u] + w[u][v]; if ( !inQ[v] ) inQ[v] = true, Q.push(v); } } } return d[t]; } void Run(int n){ Spfa(1,n,n); int cnt = 0; for (int i = n; fa[i] != nil; i = fa[i]){ path[cnt].first = fa[i]; path[cnt++].second = i; } double ans = oo; for (int i = 0; i < cnt; ++i){ int u = path[i].first, v = path[i].second; w[u][v] = w[v][u] = oo; Spfa(1,n,n); if ( d[n] < ans ) ans = d[n]; int x = pt[u].first-pt[v].first; int y = pt[u].second-pt[v].second; double dd = sqrt(x*x+y*y); w[u][v] = w[v][u] = dd; } if ( ans == oo ) printf("-1\n"); else printf("%.2lf\n", ans); } int main() { int n,m; scanf("%d%d", &n, &m); Init(n); for (int i = 1; i <= n; ++i){ int x,y; scanf("%d%d", &x, &y); pt[i] = make_pair(x,y); } for (int i = 1; i <= m; ++i){ int a,b; scanf("%d%d", &a, &b); int x = pt[a].first-pt[b].first; int y = pt[a].second-pt[b].second; double dd = sqrt(x*x+y*y); w[a][b] = w[b][a] = dd; } Run(n); return 0; }
[vijos 遭遇战]最短路, 简化的最小费用流
From:https://www.vijos.org/p/1404
Solution:依然是约束方程+差分。对于时刻Pi, 满足条件a1*X1+a2*X2+..+an*Xn <= 1, 这里ai={0,1}。最开始一直是按 = 1的方式做, 死活出错, 唉。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <math.h> #include <queue> #include <map> #include <functional> using namespace std; #define N 90002 #define M 10005 #define oo 0xefffffff #define nil (-1) typedef pair<unsigned int,int> ipair; typedef pair<pair<int,int>,int> iipair; struct Greater: public binary_function<ipair,ipair,bool>{ bool operator()(const ipair& L, const ipair& R) const{ return L.first > R.first; } }; typedef priority_queue<ipair, vector<ipair>, Greater> pri_que; struct {unsigned int w; int to,next;} nod[N+M]; int now; unsigned int adj[N], d[N]; void Init(int n){ now = 0; for (int i = 0; i <= n; ++i) adj[i] = nil; } int NewNode(int u, unsigned int w){ nod[now].to = u; nod[now].w = w; nod[now].next = nil; return now++; } void AddEdge(int u, int v, unsigned int w){ int u1 = NewNode(v, w); nod[u1].next = adj[u]; adj[u] = u1; } void Dijkstra(int s, int t, int n){ for (int i = 0; i <= n; ++i) d[i] = oo; pri_que Q; d[s] = 0; Q.push(make_pair(0, s)); while ( !Q.empty() ){ int u = Q.top().second; Q.pop(); for (int i = adj[u]; i != nil; i = nod[i].next){ int v = nod[i].to; if ( d[v] > d[u] + nod[i].w ){ d[v] = d[u] + nod[i].w; Q.push(make_pair(d[v], v)); } } } } int main() { int n, s, e; scanf("%d%d%d", &n, &s, &e); Init(e+1); for (int i = 0; i < n; ++i){ int a,b,c; scanf("%d%d%d", &a, &b, &c); if ( b < s ) continue; if ( a > e ) continue; if ( a < s ) a = s; if ( b > e ) b = e; AddEdge(a,b+1,c); } for (int i = s+1; i <= e+1; ++i) AddEdge(i,i-1,0); Dijkstra(s, e+1, e+1); if ( d[e+1] == oo ) printf("-1\n"); else printf("%u\n", d[e+1]); return 0; }
[vijos 社交网络]最短路 floyd 路径统计
From:https://www.vijos.org/p/1591
Solution:dp[i,j]表示从i到j的最短路径数。dp[i,j]=dp[i,k]*dp[k,j], d[i,j]>d[i,k]+d[k,j]; dp[i,j]+=dp[i,k]*dp[k,j], d[i,j]==d[i,k]+d[k,j]。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <math.h> #include <queue> #include <map> #include <functional> using namespace std; #define N 102 #define M 8005 #define oo 0x0fffffff #define nil (-1) int w[N][N], d[N][N]; long long dp[N][N]; void Init(int n){ for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) w[i][j] = oo; } void Floyd(int n){ for (int i = 1; i <= n; ++i){ for (int j = 1; j <= n; ++j) d[i][j] = w[i][j], dp[i][j] = (w[i][j]!=oo); d[i][i] = 0; } for (int k = 1; k <= n; ++k) for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) if ( d[i][j] > d[i][k]+d[k][j] ){ d[i][j] = d[i][k]+d[k][j]; dp[i][j] = dp[i][k]*dp[k][j]; }else if ( d[i][j] == d[i][k]+d[k][j] ){ dp[i][j] += dp[i][k]*dp[k][j]; } } void Run(int n){ Floyd(n); for (int i = 1; i <= n; ++i){ double ans = 0; for (int s = 1; s <= n; ++s) for (int t = 1; t <= n; ++t){ if ( s == t || i == s || i == t || !dp[s][t] ) continue; if ( d[i][s] + d[i][t] != d[s][t] ) continue; ans += dp[i][s]*dp[i][t]/(double)dp[s][t]; } printf("%.3f\n", ans); } } int main() { int n, m; scanf("%d%d", &n, &m); Init(n); for (int i = 0; i < m; ++i){ int a,b,c; scanf("%d%d%d", &a, &b, &c); w[a][b] = w[b][a] = c; } Run(n); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <math.h> #include <queue> #include <map> #include <functional> using namespace std; #define N 102 #define M 8005 #define oo 0x0fffffff #define nil (-1) typedef pair<unsigned int,int> ipair; struct Greater: public binary_function<ipair,ipair,bool>{ bool operator()(const ipair& L, const ipair& R) const{ return L.first > R.first; } }; typedef priority_queue<ipair,vector<ipair>,Greater> pri_que; struct {int w; int to,next;} nod[M]; int adj[N], d[N][N], now; bool outQ[N]; // 是否出过队列 long long dp[N][N]; void Init(int n){ now = 0; for (int i = 0; i <= n; ++i) adj[i] = nil; } int NewNode(int u, int w){ nod[now].to = u; nod[now].w = w; nod[now].next = nil; return now++; } void AddEdge(int u, int v, int w){ int u1 = NewNode(v, w); nod[u1].next = adj[u]; adj[u] = u1; } void Dijkstra(int S, int n){ for (int i = 0; i <= n; ++i) d[S][i] = oo, outQ[i] = false; pri_que Q; d[S][S] = 0; Q.push(make_pair(0,S)); dp[S][S] = 1; while ( !Q.empty() ){ // Dijkstra是永久定标, 出过一次就不需要处理了, 这里用stl会进多次 int u = Q.top().second; Q.pop(); if ( outQ[u] ) continue; outQ[u] = true; for (int i = adj[u]; i != nil; i = nod[i].next){ int v = nod[i].to; if ( d[S][v] > d[S][u] + nod[i].w ){ dp[S][v] = dp[S][u]; d[S][v] = d[S][u] + nod[i].w; Q.push(make_pair(d[S][v], v)); } else if ( d[S][v] == d[S][u] + nod[i].w ) dp[S][v] += dp[S][u]; } } } void Run(int n){ for (int i = 1; i <= n; ++i) Dijkstra(i, n); for (int i = 1; i <= n; ++i){ double ans = 0; for (int s = 1; s <= n; ++s) for (int t = 1; t <= n; ++t){ if ( i == s || i == t || !dp[s][t] ) continue; if ( d[i][s] + d[i][t] != d[s][t] ) continue; ans += dp[i][s]*dp[i][t]/(double)dp[s][t]; } printf("%.3f\n", ans); } } int main() { int n, m; scanf("%d%d", &n, &m); Init(n); for (int i = 0; i < m; ++i){ int a,b,c; scanf("%d%d%d", &a, &b, &c); AddEdge(a,b,c); AddEdge(b,a,c); } Run(n); return 0; }
[vijos 小D的旅行]最短路 floyd 状态记录
From:https://www.vijos.org/p/1746
Solution: d[i,j,0]表示不用加油的最小费用, 直接floyd; d[i,j,1]表示用加油的最小费用, d[i,j,1]=min(d[i,j,1],d[i,k,0]+d[k,j,0]+c[k])。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <math.h> #include <queue> #include <map> #include <functional> using namespace std; #define N 302 #define M 25005 #define oo 0x0fffffff #define nil (-1) int w[N][N], d[N][N][2], c[N]; void Init(int n){ for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) w[i][j] = oo; } void Floyd(int n){ for (int i = 1; i <= n; ++i){ for (int j = 1; j <= n; ++j){ if ( w[i][j] == oo ) d[i][j][0] = d[i][j][1] = oo; else d[i][j][0] = w[i][j], d[i][j][1] = oo; } d[i][i][0] = 0; } for (int k = 1; k <= n; ++k) for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j){ d[i][j][0] = min(d[i][j][0], d[i][k][0] + d[k][j][0]); } for (int k = 1; k <= n; ++k) for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j){ d[i][j][1] = min(d[i][j][1], d[i][k][0] + d[k][j][0]+c[k]); } } int main() { int n,m; scanf("%d%d", &n, &m); Init(n); for (int i = 1; i <= n; ++i) scanf("%d", c+i); for (int i = 1; i <= m; ++i){ int a,b,dd; scanf("%d%d%d", &a, &b, &dd); w[a][b] = w[b][a] = min(w[a][b], dd); } Floyd(n); int q; scanf("%d", &q); while ( q-- ){ int u,v; scanf("%d%d", &u, &v); if ( d[u][v][1] != oo ) printf("%d\n", d[u][v][1]); else printf("-1\n"); } return 0; }