集合位置

洛咕

一句话题意:给定(n(n<=200))个点的坐标和m条边,求次短路.

分析:这题我真的要喷死,刚开始写了个K(K=2)短路模板,愣是只有70分,看题解说每个点只能走一次,所以要判重,我就跟它一样判重,发现样例都过不去.白白刚了一个多小时.放一下这个辣鸡代码.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=1005;
int n,m,a[N*N],b[N*N],x[N],y[N],visit[N];
int tot,head[N],nxt[N*N],to[N*N];
double c[N*N],w[N*N],dis[N];
inline void add(int a,int b,double c){
	nxt[++tot]=head[a];head[a]=tot;
	to[tot]=b;w[tot]=c;
}
inline double calc(int i,int j){
	return sqrt(1.0*(x[i]-x[j])*(x[i]-x[j])+1.0*(y[i]-y[j])*(y[i]-y[j]));
}
queue<int>q;
inline void spfa(){
	memset(dis,0x3f,sizeof(dis));
	dis[n]=0;visit[n]=1;q.push(n);
	while(q.size()){
		int x=q.front();q.pop();
		visit[x]=0;
		for(int i=head[x];i;i=nxt[i]){
			int y=to[i];
			if(dis[y]>dis[x]+w[i]){
				dis[y]=dis[x]+w[i];
				if(!visit[y]){
					q.push(y);
					visit[y]=1;
				}
			}
		}
	}
}
struct ppx{
	int id;double f,g;
	bool operator <(const ppx &x)const{
		return f+g>x.f+x.g;
	}
}temp,tmp;
priority_queue<ppx>Q;
inline void kth(){
    memset(visit,0,sizeof(visit));
    temp.id=1;temp.f=0;temp.g=0;Q.push(temp);
    while(Q.size()){
        temp=Q.top();Q.pop();
        int u=temp.id;++visit[u];
        if(visit[u]==2&&u==n){
            printf("%.2lf
",temp.f+temp.g);
            return;
        }
        else if(visit[u]>2)continue;
        for(int i=head[u];i;i=nxt[i]){
            tmp.id=to[i];
            tmp.f=temp.f+w[i];
            tmp.g=dis[to[i]];
            Q.push(tmp);
        }
    }
    puts("-1");
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;++i)x[i]=read(),y[i]=read();
	for(int i=1;i<=m;++i){
		a[i]=read();b[i]=read();
		c[i]=calc(a[i],b[i]);
		add(a[i],b[i],c[i]);
		add(b[i],a[i],c[i]);
	}
	spfa();kth();
    return 0;
}

进入正题,我们先跑一遍完整的SPFA,预处理出最短路所经过的路径,用一个(pre[])记录前驱就好.然后每次删一条最短路径上的边再跑SPFA,得到的最小答案就是最短路了.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=505;
int n,m;
int x[N],y[N],visit[N],pre[N];
int tot,head[N],nxt[N*N],to[N*N];
double w[N*N],dis[N];
inline double calc(int i,int j){
	return sqrt(1.0*(x[i]-x[j])*(x[i]-x[j])+1.0*(y[i]-y[j])*(y[i]-y[j]));
}
inline void add(int a,int b,double c){
	nxt[++tot]=head[a];head[a]=tot;
	to[tot]=b;w[tot]=c;
}
inline void spfa(int x,int y){
	for(int i=1;i<=n;++i)dis[i]=1e18;
	memset(visit,0,sizeof(visit));
	queue<int>q;dis[1]=0;visit[1]=1;q.push(1);
	while(q.size()){
		int u=q.front();q.pop();
		visit[u]=0;
		for(int i=head[u];i;i=nxt[i]){
			int v=to[i];
			if((u==x&&v==y)||(u==y&&v==x))continue;//如果这条边被删了,就不能走
			if(dis[v]>dis[u]+w[i]){
				dis[v]=dis[u]+w[i];
				if(!x&&!y)pre[v]=u;//第一次SPFA,记录路径
				if(!visit[v]){
					visit[v]=1;
					q.push(v);
				}
			}
		}
	}
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;++i)x[i]=read(),y[i]=read();
	for(int i=1;i<=m;++i){
		int a=read(),b=read();
		double c=calc(a,b);//计算两点间距离
		add(a,b,c);add(b,a,c);
	}
	spfa(0,0);double ans=1e18;
	for(int i=n;pre[i];i=pre[i]){
		spfa(pre[i],i);//每次删一条边
		ans=min(ans,dis[n]);
	}
	if(ans==1e18)puts("-1");
	else printf("%.2lf
",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11545465.html