网上似乎没多少单源次短路的学习,今天做了道例题,写一下做法
例题:P2829 大逃离 和 P2865 [USACO06NOV] Roadblocks G
以第一题为例:
- 我们只需要求出以 1 和 n 为源点各点的最短路,然后枚举每一条边(因为是双向的,所以把他拆成两个有向边)然后比较 和 的大小取最小值就行,但是需要注意,如果遇到 d 时要跳过,因为我们求的是次短路 (dis[1][x]表示以1为源点,到x的最短路,dis[2][y]表示以n为源点,到y的最短路,z是边的长度,ans是答案)
这题有一个特殊的地方,就是要处理每个点直接相连的点的个数,直接通过 hav[x][y]记录两个点是否有直接点相连,link[]记录直接相连的点的个数,在读入边的时候如果两点有相连,先二者link[x]--,link[y]--,然后再二者++,如果没有相连则不需要自减,具体可以见代码,较容易理解
#include<bits/stdc++.h>
#define mp(x,y) make_pair(x,y)
using namespace std;
const int F=300000;
const int P=5010;
const int inf=1e9;
struct sl1{
int x,y,z;
}len[F];
int tot,n,m,k,minn=inf;
int head[P],ver[F],nxt[F],edg[F],hav[P][P],link[P],dis[3][P],v[P];
priority_queue<pair<int,int>> q;
void add(int x,int y,int z){
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
edg[tot]=z;
}
bool checkpoi(int y){
return (link[y]>=k||y==1||y==n);
}
void djstl(int x,int ml){
for(int i=1;i<=n;i++){
dis[ml][i]=inf;
v[i]=0;
}
dis[ml][x]=0;
q.push(mp(0,x));
while(q.size()){
int x=q.top().second;
q.pop();
if(v[x]) continue;
v[x]=1;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i],z=edg[i];
if(checkpoi(y)&&dis[ml][x]+z<dis[ml][y]){
dis[ml][y]=dis[ml][x]+z;
q.push(mp(-dis[ml][y],y));
}
}
}
}
void fst(){
//freopen("try.in","r",stdin);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
}
int main(){
fst();
cin>>n>>m>>k;
for(int x,y,z,i=1;i<=m;i++){
cin>>x>>y>>z;
if(hav[x][y]||hav[y][x]){
link[x]--;
link[y]--;
}
len[i+m].y=len[i].x=x;
len[i+m].x=len[i].y=y;
len[i+m].z=len[i].z=z;
hav[x][y]=hav[y][x]=1;
link[x]++; link[y]++;
add(x,y,z); add(y,x,z);
}
djstl(1,1);
djstl(n,2);
for(int i=1;i<=2*m;i++){
int x=len[i].x,y=len[i].y,z=len[i].z;
if(dis[1][x]+dis[2][y]+z<minn&&dis[1][x]+dis[2][y]+z!=dis[1][n]&&checkpoi(x)&&checkpoi(y)){
minn=dis[1][x]+dis[2][y]+z;
}
}
if(minn==inf) cout<<-1<<endl;
else cout<<minn<<endl;
return 0;
}