浅谈最短路【复习】

floyd:

http://acm.hdu.edu.cn/showproblem.php?pid=1599

找最小环:

code by wzxbeliever:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
#define INF 1<<24
using namespace std;
const int maxn=105;
int n,m,minn;
int dis[maxn][maxn],mp[maxn][maxn]; 
il void init(){
	for(ri i=1;i<=n;i++)
	for(ri j=1;j<=n;j++)
	dis[i][j]=dis[j][i]=mp[i][j]=mp[j][i]=INF;
	for(ri i=1;i<=m;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		if(mp[u][v]>w)mp[u][v]=mp[v][u]=dis[u][v]=dis[v][u]=w;
	}
	minn=INF;return;
}
il void solve(){
	for(ri k=1;k<=n;k++){
		for(ri i=1;i<k;i++)
		if(mp[i][k]^INF)
		for(ri j=1;j<i;j++)
		if(mp[j][k]^INF&&dis[i][j]^INF)
		if(minn>dis[i][j]+mp[i][k]+mp[k][j])
		minn=dis[i][j]+mp[i][k]+mp[k][j];
		
		for(ri i=1;i<=n;i++)
		if(dis[i][k]^INF)
		for(ri j=1;j<=n;j++)
		if(dis[j][k]^INF)
		if(dis[i][k]+dis[k][j]<dis[i][j])
		dis[i][j]=dis[i][k]+dis[k][j];
	}
	if(minn^INF)printf("%d\n",minn);
	else printf("It's impossible.\n");
}
int main(){
	while(scanf("%d%d",&n,&m)!=EOF){init();solve();}
	return 0;
}

最小密度路径:

题目描述:

给出了一张有N个点M条边的加权有向无环图,接下来有Q个询问,每个询问包括2个节点X和Y,要求算出从X到Y的一条路径,使得密度最小(密度的定义为,路径上边的权值和除以边的数量)

n<=50

分析:

看数据看题目,一般的floyd是不行的

那就再加一维边数

f(i,j,k)=Min{f(i,h,g)+f(h,j,k-g)}。

五维的,会超时

注意到f(i,j,k)的选择路径V1-V2-...-Vk时,其实进行了很多重复决策

比如f(i,V2,2)+f(V2,j,k-2)与f(i,V6,6)+f(V6,j,k-6)本质上都在指明这条路径是最优的

所以只用找到一个决策点就好

即可

code by wzxbeliever:

#include<stdio.h>
using namespace std;
#define INF 1000000000
int n,m,q;
int dis[60][60][1010];
int main()
{
    int i,j,k,l;
    scanf("%d %d",&n,&m);
    for(l=1;l<=m;l++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)dis[i][j][l]=INF;
    for(i=1;i<=m;i++)
    {
        long long a,b,w;
        scanf("%lld %lld %lld",&a,&b,&w);
        if(dis[a][b][1]>w)dis[a][b][1]=w;
    }
    for(l=2;l<=m;l++)
        for(k=1;k<=n;k++)
            for(i=1;i<=n;i++)
                for(j=1;j<=n;j++)
                    if(dis[i][j][l]>dis[i][k][l-1]+dis[k][j][1])
                        dis[i][j][l]=dis[i][k][l-1]+dis[k][j][1];
    scanf("%d",&q);
    while(q--)
    {
        int x,y;
        double ans=INF,min=INF;
        scanf("%d %d",&x,&y);
        for(l=1;l<=n;l++)
        {
            if(dis[x][y][l]<INF)ans=double(dis[x][y][l])/double(l);
            if(ans<min)min=ans;
        }
        if(min==INF)
            printf("OMG!\n");
        else
            printf("%.3lf\n",min);
    }
}

[10.12模拟赛] 旅程

题目描述

神即将带领一些人去他们的孤寂之境,由于这个世界的不稳定,地点之间的有向道路会不定期地毁坏,出于工作准备,神想知道在某些道路毁坏之后某两点之间的最短路。
就是给定一个有向图,现有两个操作,操作 1 是删除一条边(一条边可重复删除),操作 2是询问两个点之间的最短路。

分析:

倒着来做,我们首先预处理出来是在所有删边操作全部结束后的任意两点最短路,那么每次对于一个删边操作,实际上变成了加边,我们要用加的这条边去更新其他点,因为已经确定是由新加进来的边去更新其他点,所以我们可以省去floyed枚举断电那一重循环,O(n2)即可,在加上删边操作最多200个,总复杂度O(n3)

注意:有坑点,题目说可以重复删边,所以我们要记录一下一条边是什么时候最早被删的,只有到那个时候才算真正加上这条边

code:

#include<bits/stdc++.h>
#define rg register
#define il inline
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
#define lol long long
#define in(i) (i=read())
using namespace std;

const lol N=210,M=1e5+10,inf=1e15;

lol read() {
    lol ans=0,f=1; char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
    return ans*=f;
}
lol n,m;
lol w[N][N],del[N][N],vis[N][N],ans[M];
struct AQ {
    lol op,x,y;
}t[M];
int main()
{
    //freopen("journey.in","r",stdin);
    //freopen("journey.out","w",stdout);
    in(n),in(m);
    memset(w,127,sizeof(w));
    memset(vis,127,sizeof(vis));
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) in(w[i][j]),del[i][j]=w[i][j];
    
    for(int i=1;i<=n;i++) w[i][i]=del[i][i]=0;
    
    for(lol i=1;i<=m;i++) {
        in(t[i].op),in(t[i].x),in(t[i].y);
        if(t[i].op==1) {
            del[t[i].x][t[i].y]=inf;
            vis[t[i].x][t[i].y]=min(vis[t[i].x][t[i].y],i);//第一次删边
        }
    }
    
    for(lol k=1;k<=n;k++) 
        for(lol i=1;i<=n;i++) 
            for(lol j=1;j<=n;j++) {
                if(i==k || j==k || i==j) continue;
                del[i][j]=min(del[i][j],del[i][k]+del[k][j]);
            }

    
    for(lol i=m;i>=1;i--) {
        if(t[i].op==2) ans[i]=del[t[i].x][t[i].y];
        else {
            if(vis[t[i].x][t[i].y]!=i) continue;
            
            del[t[i].x][t[i].y]=min(del[t[i].x][t[i].y],w[t[i].x][t[i].y]);//原边权与已经跑出来的最短路取个最小值
            for(lol p=1;p<=n;p++) {
                for(lol q=1;q<=n;q++) {
                    del[p][q]=min(del[p][q],del[p][t[i].x]+del[t[i].y][q]+del[t[i].x][t[i].y]);
                }
            }
        }
    }
    for(lol i=1;i<=m;i++) {
        if(t[i].op==2) printf("%lld\n",ans[i]);
    }
}

迪杰斯特拉:

test by jklover:

分析:

正向跑一遍最短路,建反图跑一遍最短路

判断每条边是否在1--->n必经之路&&这条边在最短路上---->1

否则如果最短路变小了------>-1

否则 就不变------>0

注意最短路的时候一定要加vis数组,因为有边权为0的情况

code by wzxbeliever&jklover:

//%std
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
	int out=0,fh=1;
	char jp=getchar();
	while ((jp>'9'||jp<'0')&&jp!='-')
		jp=getchar();
	if (jp=='-')
		fh=-1,jp=getchar();
	while (jp>='0'&&jp<='9')
		out=out*10+jp-'0',jp=getchar();
	return out*fh;
}
const int inf=1e9;
const int MAXN=2e5+10;
int n,m;
struct road{
	int dis,id;
	bool friend operator<(road a,road b){return a.dis>b.dis;}
}tmp;
priority_queue<road>Q;
struct Graph
{
	int ecnt,head[MAXN],to[MAXN],nx[MAXN],val[MAXN];
	int dis[MAXN];
	unsigned long long cnt[MAXN];
	void init()
	{
		ecnt=0;
		for(int i=1;i<=n;++i)
			head[i]=0;	
	}
	void addedge(int u,int v,int w)
	{
		++ecnt;
		to[ecnt]=v;
		nx[ecnt]=head[u];
		val[ecnt]=w;
		head[u]=ecnt;
	}
	int vis[MAXN];
	void Dijkstra(int S)
	{
		for(int i=1;i<=n;++i)
		{
			dis[i]=inf;
			cnt[i]=0;
			vis[i]=0;
		}
		tmp.id=S;tmp.dis=0;dis[S]=0;cnt[S]=1;
	Q.push(tmp);
	while(!Q.empty()){
		int u=Q.top().id;Q.pop();
		if(vis[u])
			continue;
		vis[u]=1;
		for(int i=head[u];i;i=nx[i]){
			int v=to[i];
			if(dis[v]-dis[u]>val[i]){
				dis[v]=dis[u]+val[i];
				tmp.id=v;tmp.dis=dis[v];
				Q.push(tmp);cnt[v]=cnt[u];
			}
			else if(dis[v]==dis[u]+val[i])cnt[v]+=cnt[u];
		}
	}
	}
}Gs,Gt;
int U[MAXN],V[MAXN],W[MAXN];
int main()
{
	freopen("seclusion.in","r",stdin);
	freopen("seclusion.out","w",stdout);
	n=read(),m=read();
	int S=1,T=n;
	for(int i=1;i<=m;++i)
	{
		int u=read(),v=read(),w=read();
		Gs.addedge(u,v,w);
		Gt.addedge(v,u,w);
		U[i]=u,V[i]=v,W[i]=w;
	}
	Gs.Dijkstra(S);
	//assert(Gs.dis[T]<inf);
	Gt.Dijkstra(T);
	for(int i=1;i<=m;++i)
	{
		int u=U[i],v=V[i],w=W[i];
		if(Gs.cnt[u]*Gt.cnt[v]==Gs.cnt[T] && Gs.dis[u]+Gt.dis[v]+w==Gs.dis[T])
			puts("1");
		else if(Gs.dis[v]+Gt.dis[u]+w<Gs.dis[T])
			puts("-1");
		else
			puts("0");
	}
	return 0;
}

题目大意:求严格次短路

分析:

用两边最短路,分别从1,n跑一遍,在枚举每条边,让1到n的路径中必须包含这条边,再与1到n的最短路作比较,并更新最优值。

code:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int N=5005,M=100005;
struct Edge {
    int to,next;
    long long weight;
}e[M*2];
int n,m;
int h[N],cnt,k;
long long d[2][N],vis[N],ans;
void add(int x,int y,long long z) {
    e[++cnt]=(Edge){y,h[x],z};
    h[x]=cnt;
}
void spfa(int v0) {
    memset(vis,0,sizeof(vis));
    queue<int> q;
    q.push(v0);
    vis[v0]=1;
    d[k][v0]=0;
    while (!q.empty()) {
        int x=q.front();
        q.pop();
        vis[x]=0;
        for (int i=h[x];i;i=e[i].next) {
            int y=e[i].to;
            if (d[k][x]+e[i].weight<d[k][y]) {
                d[k][y]=d[k][x]+e[i].weight;
                if (!vis[y]) {
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
    }
    ++k;
}
int main() {
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++) {
        int a,b;
        long long c;
        scanf("%d%d%lld",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    for (int i=1;i<=n;i++)
        d[0][i]=d[1][i]=(1ULL<<63)-1LL;
    spfa(1);
    spfa(n);
    ans=(1ULL<<63)-1LL;
    for (int i=1;i<=n;i++) {
        for (int j=h[i];j;j=e[j].next) {
            int y=e[j].to;
            long long p=d[0][i]+e[j].weight+d[1][y];
            if (p>d[0][n]) ans=min(ans,p);
        }
    }
    printf("%lld",ans);
    return 0;
}
posted @ 2019-11-14 20:07  wzx_believer  阅读(158)  评论(0编辑  收藏  举报