问题 A: k短路
时间限制: 2 Sec 内存限制: 256 MB
题目描述
有n个城市和m条单向道路,城市编号为1~n。每条道路连接两个不同的城市,且任意两条道路要么起点不同要么终点不同,因此n和m满足m<=n(n-1)。给定两个城市a和b,可以给a到b的所有简单路(所有城市最多经过一次,包括起点和终点)排序:先按长度从小到大排序,长度相同时按照字典序从小到大排序。你的任务是求出a到b的第k短路。
输入
输入第一行包含五个正整数n, m, k, a, b。以下m行每行三个整数u, v, l,表示从城市u到城市v有一条长度为l的单向道路。100%的数据满足:2<=n<=50, 1<=k<=200
输出
如果a到b的简单路不足k条,输出No,否则输出第k短路:从城市a开始依次输出每个到达的城市,直到城市b,中间用减号”-“分割。
样例输入
5 20 10 1 5
1 2 1
1 3 2
1 4 1
1 5 3
2 1 1
2 3 1
2 4 2
2 5 2
3 1 1
3 2 2
3 4 1
3 5 1
4 1 1
4 2 1
4 3 1
4 5 2
5 1 1
5 2 1
5 3 1
5 4 1
样例输出
1-2-4-3-5
提示
第一个例子有5个城市,所有可能出现的道路均存在。从城市1到城市5一共有5条简单路
Code
#include <bits/stdc++.h>
using namespace std;
const int mxn = 51, mxm = 3001, mxk = 201, INF = ~0U >> 2;
struct edge {
int u,v,w,pre,nex;
}e[mxn+mxm],f[mxn+mxm];
struct data{
int u,v,w;
bool operator < (data B) const {
return v<B.v;
}
}tmp[mxm];
int n,m,k,a,b,dist[mxn],Q[mxn+1],tot;
int z,z0,No,len1,visit[mxn],tmps[mxn],len[mxk],sheet[mxk][mxn],sum[mxk],tmpl,ansl,ans[mxn];
bool vis[mxn],ok;
void read(int &k) {
int f=1;
char ch=getchar();
while ((ch<48||ch>57)&&ch!='-') ch=getchar();
if (ch=='-') f=-1,ch=getchar();
k=0;
while (ch>=48&&ch<=57) k=k*10+ch-48,ch=getchar();
k*=f;
}
void add_edge(int u,int v,int w) {
e[tot]=(edge){u,v,w,e[u].pre,u};
e[u].pre=tot;
e[e[tot].pre].nex=tot;
f[tot]=(edge){v,u,w,f[v].pre,v};
f[v].pre=tot;
f[f[tot].pre].nex=tot++;
}
void dfs(int x, int ss) {
if (x == b) {
if (ss <= z) {
sum[No] = ss;
len[No] = len1;
for (int i=0; i<len1; ++i) sheet[No][i] = tmps[i];
No++;
if (No == k) ok = 1;
}
else if (ss < z0) z0 = ss;
return;
} else {
int h0 = ss + dist[x];
if (h0 > z) {
if (h0 < z0) z0 = h0;
return;
}
visit[x] = ++tmpl;
Q[0] = x;
int u, v, l = -1, r = 0;
while (l<r) {
u = Q[++l];
for (int p=e[u].nex; p!=u; p=e[p].nex) {
v = e[p].v;
if (!vis[v] && visit[v]!=tmpl) {
visit[v] = tmpl;
Q[++r] = v;
}
}
}
if (visit[b] != tmpl) return;
for (int p=e[x].nex; p!=x; p=e[p].nex) {
v = e[p].v;
if (!vis[v]) {
vis[v] = 1;
tmps[len1++] = v;
dfs(v, ss + e[p].w);
if (ok) return;
else {
len1--;
vis[v] = 0;
}
}
}
}
}
void solve() {
z = dist[a], tmpl = 0;
int No0 = 0;
while (1) {
z0 = INF, No = 0;
memset(vis,0,sizeof(vis));
memset(visit,0,sizeof(visit));
vis[a] = 1;
len1 = 1;
tmps[0] = a;
dfs(a, 0);
if (ok) {
No0 = k - No0;
for (int i=0; i<k; ++i)
if (sum[i] == z) {
No0--;
if (!No0) {
ansl = len[i];
for (int j=0; j<len[i]; ++j) ans[j] = sheet[i][j];
}
}
break;
} else
if (z0 == INF) break;
else {
No0 = No;
z = z0;
}
}
}
int main() {
read(n); read(m); read(k); read(a); read(b);
a--;
b--;
for (int i=0;i<n;++i) e[i].pre=e[i].nex=f[i].pre=f[i].nex=i;
tot=n;
for (int i=0;i<m;++i) {
read(tmp[i].u); read(tmp[i].v); read(tmp[i].w);
tmp[i].u--;
tmp[i].v--;
}
sort(tmp,tmp+m);
for (int i=0;i<m;++i) add_edge(tmp[i].u,tmp[i].v,tmp[i].w);
for (int i=0;i<n;++i) {
vis[i]=0;
dist[i]=INF;
}
vis[b]=1;
dist[b]=0;
Q[0]=b;
int l=0,r=0;
while (!(!l&&r==n||l==r+1)) {
int u=Q[l], x=dist[u];
for (int p=f[u].nex; p!=u; p=f[p].nex) {
int v=f[p].v, y=x+f[p].w;
if (y<dist[v]) {
dist[v]=y;
if (!vis[v]) {
vis[v]=1;
Q[r==n ? r=0 : ++r]=v;
}
}
}
vis[u]=0;
l==n ? l=0 : l++;
}
solve();
if (ok) {
printf("%d", ans[0] + 1);
for (int i = 1; i < ansl; ++i) printf("-%d",ans[i] + 1);
printf("\n");
} else printf("No\n");
return 0;
}
/**************************************************************
Problem: 4131
User: WC006
Language: C++
Result: 正确
Time:1420 ms
Memory:1904 kb
****************************************************************/