Description
給出一個n個點m條邊的帶邊權無向圖
定義1到n的K最短路爲所有1到n的路徑中,路徑上的邊權前K大和的最小值。
求K=1~n的最短路。
n,m<=3000,邊權<=10^9
Solution
首先我們考慮枚舉第K大的邊,將小於這條邊的所有邊記爲0(相等的話強行給一個順序)
考慮一個樸素的DP,表示當前到i號點,經過了j條大於枚舉這條邊的邊,是否經過這條邊。
直接分層圖轉移即可。
事實上我們完全不需要記第二維
枚舉第K大的邊e(可以是0),然後將所有邊權賦爲
直接跑一遍最短路,用最後的距離加上更新所有的答案。
正確性顯然,因爲原本的答案不會變,而不合法的部分只會更劣。
採用dijkstra,就在的時間複雜度解決。
Code
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
const int N=3005;
typedef long long LL;
using namespace std;
int n,m,fs[N],nt[2*N],dt[2*N],pr[2*N],m1,lim;
void link(int x,int y,int z)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
pr[m1]=z;
}
LL ans[N],dis[N];
bool bz[N];
struct node
{
int x;LL v;
friend bool operator <(node x,node y)
{
return x.v>y.v;
}
};
priority_queue<node> h;
int main()
{
cin>>n>>m;
memset(ans,107,sizeof(ans));
fo(i,1,m)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
link(x,y,z),link(y,x,z);
}
fo(q,1,m+1)
{
lim=(q==m+1)?0:pr[2*q];
memset(dis,107,sizeof(dis));
memset(bz,0,sizeof(bz));
dis[1]=0;
while(!h.empty()) h.pop();
h.push((node){1,0});
fo(t,1,n-1)
{
while(bz[(h.top()).x]) h.pop();
int k=(h.top()).x;
bz[k]=1,h.pop();
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];LL c=dis[k]+max(0,pr[i]-lim);
if(dis[p]>c) dis[p]=c,h.push((node){p,c});
}
}
fo(i,1,n) ans[i]=min(ans[i],(LL)i*lim+dis[n]);
}
fod(i,n,1) printf("%lld\n",ans[i]);
}