Skip to content

k短路 ​

cpp
// A* O(kn^2logn)
#include<bits/stdc++.h>
#define x0 x_0
#define x1 x_1
#define y0 y_0
#define y1 y_1
#define yn y_n
#define d0 d_0
#define d1 d_1
#define j0 j_0
#define j1 j_1
#define k0 k_0
#define k1 k_1
#define LL long long
#define LD long double
using namespace std;
int n,m,k,a,b;
struct node{
	int v;
	LL w;
	bool operator < (const node &o)const{
		return w>o.w;
	}
};
struct dij_node{
	int v;
	LL w;
	bool operator < (const dij_node &o)const{
		return w>o.w;
	}
};
struct STRING{
	char p[50];
	int l;
	bool operator > (const STRING &o)const{
		int cst=min(l,o.l);
		for(int i=0;i<cst;++i){
			if(p[i]>o.p[i]) return 1;
			else if(p[i]<o.p[i]) return 0;
		}
		if(l>=o.l) return 1;
	}
};
struct point{
	int v;
	LL g,h;
	STRING s;
	LL S;
	bool operator < (const point &o)const{
		if((g+h)^(o.g+o.h)) return g+h>o.g+o.h;
		return s>o.s;
	}
};
vector<node> G[60],G2[60];
char inc[60];
int decc[128];
char incode_int(int x){
	return (x<=26 ? 'A'+x-1 : 'a'-26+x-1);
}
int decode_char(char x){
	return (x>='A' && x<='Z' ? x-'A'+1 : x-'a'+27);
}
LL dis[60];
bool f[60];
const LL mex=1e15;
void dijkstra(int s,LL SSS){ // Part 1 部分
	for(int i=1;i<=n;++i) dis[i]=mex;
	dis[s]=0;
	memset(f,0,sizeof(f));
	priority_queue<dij_node> q;
	q.push({s,0});
	while(q.size()){
		dij_node dt=q.top();
		q.pop();
		if(f[dt.v]) continue;
		int x=dt.v;
		f[x]=1;
		for(int i=0;i<(int)G2[x].size();++i){
			int u=G2[x][i].v;
			if(SSS&(1LL<<u)) continue;
			LL w=G2[x][i].w;
			if(dis[u]>dis[x]+w){
				dis[u]=dis[x]+w;
				q.push({u,dis[u]});
			}
		}
	}
	return ;
}
int cnt[60];
void Astar(int s,int t,int k){
	memset(cnt,0,sizeof(cnt));
	priority_queue<point> q;
	STRING psh;
	psh.p[psh.l++]=incode_int(s);
	q.push({s,0,0,psh,1LL<<s});
	while(q.size()){
		point dt=q.top();
		q.pop();
		int x=dt.v;
		++cnt[x];
		if(x==t && cnt[x]==k){
			int len=dt.s.l;
			cout<<decc[dt.s.p[0]];
			for(int i=1;i<len;++i) cout<<"-"<<decc[dt.s.p[i]];
			cout<<"\n";
			exit(0);
		}
		dijkstra(t,dt.S);
		for(int i=0;i<(int)G[x].size();++i){
			int u=G[x][i].v;
			LL w=G[x][i].w;
			if(dt.S&(1LL<<u)) continue;
			dt.s.p[dt.s.l++]=inc[u];
			q.push((point){u,dt.g+w,dis[u],dt.s,dt.S|(1LL<<u)});
			--dt.s.l;
		}
	}
	cout<<"No\n";
	exit(0);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>k>>a>>b;
	for(int i=1;i<=m;++i){
		int u,v;
		LL w;
		cin>>u>>v>>w;
		G[u].push_back({v,w});
		G2[v].push_back({u,w});
	}
	for(int i=1;i<=52;++i) inc[i]=incode_int(i);
	for(int i=0;i<128;++i) decc[i]=decode_char((char)i);
	dijkstra(b,0);
	if(dis[a]>=mex) cout<<"No\n",exit(0);
	Astar(a,b,k);
	return 0;
}
// O(n^3k)
#include<cstdio>
#include<vector>
#include<ext/pb_ds/priority_queue.hpp>
#include<bitset>
#include<stack>
#include<cstring>
namespace Use {
	using std::vector;
	using std::min;
	using std::max;
	using __gnu_pbds ::priority_queue;
	using std::bitset;
	using std::stack;
	bool operator <(const vector<int>&a, const vector<int>&b) {
		unsigned sx = a.size(), sy = b.size(), siz = min(sx, sy);
		for (unsigned i = 0; i < siz; ++i) 
			if (a[i] > b[i]) return 0;
			else if (a[i] < b[i]) return 1;
		return sx < sy;
	}
	bool operator >(const vector<int>&a, const vector<int>&b) {
		unsigned sx = a.size(), sy = b.size(), siz = min(sx, sy);
		for (unsigned i = 0; i < siz; ++i) 
			if (a[i] > b[i]) return 1;
			else if (a[i] < b[i]) return 0;
		return sx > sy;
	}//保证字典序的途径
}
using namespace Use;
typedef long long ll;
const int maxn = 55, maxm = 2505, inf = 0x3f3f3f3f;
struct Edge {
	int to, dis, next;
} edge[maxm];

int head[maxn], len;
inline void insert(const int &u, const int &v, const int &w) {
	edge[++len] = {v, w, head[u]}, head[u] = len;
}

int n, dis[maxn], to;
bitset<maxn>onTheSP;//onTheSP[u] = 1 表示点 u 在最短路上
bitset<maxn>vis;//vis[u] = 1 表示在求最短路时 u 在 close 表上
bitset<maxm>isErased;//isErased[i] = 1 表示有向边 i 被删除了
stack<int>s;// 算出最短路时经过的点的栈,满足 LIFO,辅助求最短路路径
struct Path {
	int start;//路径的起始点
    int len;//当前将算出的最短路长度
    int lastplace;//已经走过的属于前缀的点个数
    int lastlen;//已经走过的前缀的长
	vector<int>path;//当前将算出的最短路路径
    vector<int>erasedEdges;//已经被删去的边
	Path (const int &_s = 0) : start(_s), len(0), lastplace(0), lastlen(0) {
		path.clear(), erasedEdges.clear();
	}
	void Init(const int &_s)  {
		start = _s, len = lastplace = lastlen = 0, path.clear(), erasedEdges.clear();
	}
	bool operator <(const Path &b) const & {
		return len == b.len ? path > b.path : len > b.len;
	}//堆使得 < 反向
	bool Dijkstra() {
		for (int i = 1; i <= n; ++i) dis[i] = inf;//路径初始
		isErased.reset();
		onTheSP.reset();
		vis.reset();
		dis[start] = 0;
		for (int i = 0, k = lastplace = path.size(); i < k; ++i) vis[path[i]] = 1;
		for (int i : erasedEdges) isErased[i] = 1;
		for (; !s.empty(); s.pop());
		for (int __ = 1, u, w; __ <= n; ++__) {//跑 Dijkstra
			u = 0;
			w = inf;
			for (int i = 1; i <= n; ++i) if (!vis[i] && w > dis[i]) w = dis[i], u = i;
			if (!u || w == inf) break;
			vis[u] = 1;
			s.push(u);
			for (int i = head[u], v, d; i; i = edge[i].next)
				if (!isErased[i] && dis[v = edge[i].to] > (d = w + edge[i].dis))
					dis[v] = d;
		}
		if (!vis[to] || dis[to] == inf) return 0;// 判断从点 start 开始能不能到达终点 to
		for (onTheSP[to] = 1; !s.empty(); s.pop()) //找到全部的在当前最短路上的点
			for (int u = s.top(), i = head[u], w = dis[u], v, d; i; i = edge[i].next)
				if (!isErased[i] && onTheSP[v = edge[i].to] && dis[v] == (d = w + edge[i].dis)) {
					onTheSP[u] = 1;//通过判断能否松弛下一条边判断是否在最短路 
					break;
				}
		for (int u = start, w; u != to; ) {
			path.emplace_back(u), w = dis[u];
			for (int i = head[u], v = (u = n + 1, 0); i; i = edge[i].next)
				if (!isErased[i] && onTheSP[v = edge[i].to] && dis[v] == w + edge[i].dis)
					u = min(u, v);//保证字典序 
		}
		path.emplace_back(to);
        len = lastlen + dis[to];//合并之前的路径前缀
		return 1;
	}
	void Write() {
		for (int u : path) printf("%d%c", u, u == to ? 10 : '-');
	}
} U, V;

int k, start;
priority_queue<Path>h;
void kthShortestPath() {
	if ((U = {start}).Dijkstra()) h.push(U);
	for (int siz, u, v, w, e; !h.empty() && --k; ) {
		U = h.top();
        h.pop();
        siz = U.path.size() - 1;//减掉 1 保证不走到终点 to
        w = U.lastlen;//直接从上一次走到的前缀的最后点开始走
		for (int p = U.lastplace/*同理*/; p < siz; ++p) {
			u = U.path[p];
			v = U.path[p + 1];
			for (e = head[u]; e; e = edge[e].next) if (edge[e].to == v) break;//取出边 e
			V.Init(u);//使路径以点 u 为起点
			V.erasedEdges = U.erasedEdges;//加入已经不能走的边 
			V.erasedEdges.emplace_back(e);//去除边 e
			V.lastlen = w;//记录已走路径前缀
			w += edge[e].dis;//之后路径前缀长增加了 edge[e].dis
			for (int i = 0; i < p; ++i) V.path.emplace_back(U.path[i]);//加入已走的点
			if (V.Dijkstra()) h.push(V);//在这些条件下,试图扩展路径并入队
		}
	}
	if (h.empty()) printf("No");
	else U = h.top(), U.Write();
}

int main() {
	int m;
	scanf("%d%d%d%d%d", &n, &m, &k, &start, &to);
	for (int u, v, w; m; --m) scanf("%d%d%d", &u, &v, &w), insert(u, v, w);
	kthShortestPath();
	return 0;
}