zoukankan      html  css  js  c++  java
  • 最短路

    最短路

    单源最短路

    dijkstra堆优化 O(nlogn)

    不能处理负边权

    每次从队里拽出来打标记

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int N=1e5+10;
    const int M=1e6+10;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m,s;
    int hd[N],to[M],nxt[M],tot,w[M];
    inline void add(int x,int y,int z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
    }
    struct node{
    	int u,val;
    	bool operator < (const node &w) const {
    		return val>w.val;
    	}
    	node(){}
    	node(int u_,int v_):u(u_),val(v_){}
    };
    priority_queue< node >q;
    int dis[N];
    bool vis[N];
    void dij() {
    	for(int i=0;i<=n;i++) dis[i]=0x7fffffff;
    	dis[s]=0;
    	q.push(node(s,0));
    	while(q.size()) {
    		int x=q.top().u;q.pop();
    		if(vis[x]) continue;
    		vis[x]=1;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y]>dis[x]+w[i]) {
    				dis[y]=dis[x]+w[i];
    				if(!vis[y]) q.push(node(y,dis[y]));
    			}
    		}
    	}
    } 
    int main() {
    	n=read();m=read();s=read();
    	for(int i=1,u,v,ww;i<=m;i++) {
    		u=read(),v=read(),ww=read();
    		add(u,v,ww);
    	}
    	dij();
    	for(int i=1;i<n;i++) 
    		printf("%d ",dis[i]);
    	printf("%d
    ",dis[n]);
    	return 0;
    }
    

    线段树优化建图

    spfa 稀疏图 (O(n)) 稠密 (O(n*m))

    每次从队里拽出来是(vis)标记清空

    然后入队打标记

    void spfa() {
    	memset(dis,0x3f,sizeof(dis));
    	q.push(s);
    	dis[s]=0;
    	while(q.size()) {
    		int x=q.front();q.pop();
    		vis[x]=0;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y]>dis[x]+w[i]) {
    				dis[y]=dis[x]+w[i];
    				if(!vis[y]) {vis[y]=1;q.push(y);}
    			}
    		}
    	}
    } 
    

    判负环

    开个 cnt [ ] ,更新(一定是入队次数) >n 次说明有负环

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int N=2005;
    const int M=1e5+10;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m;
    int hd[N],to[M],tot,nxt[M],w[M];
    inline void add(int x,int y,int z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
    }
    queue<int>q;
    int dis[N],cnt[N];
    bool vis[N];
    bool spfa() {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	memset(cnt,0,sizeof(cnt));
    	while(q.size()) q.pop();
    	q.push(1);
    	dis[1]=0;cnt[1]=1;
    	while(q.size()) {
    		int x=q.front();q.pop();
    		vis[x]=0;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y]>dis[x]+w[i]) {
    				dis[y]=dis[x]+w[i];
    				if(++cnt[y]>n) return 1;
    				if(!vis[y]) {vis[y]=1;q.push(y);}
    			}
    		}
    	}
    	return 0;
    } 
    
    int main() {
    	int T=read();
    	while(T--) {
    		tot=0;
    		memset(hd,0,sizeof(hd));
    		n=read();m=read();
    		for(int i=1;i<=m;i++) {
    			int u=read(),v=read(),w=read();
    			if(w>=0) add(u,v,w),add(v,u,w);
    			else add(u,v,w);
    		}
    		puts(spfa()?"YES":"NO");
    	}
    	return 0;
    }
    
    

    SLF

    将原队列改成双端队列,对要加入队列的点 (p),如果 (dis[p]) 小于队头元素(u)(dist[u]),将其插入到队头,否则插入到队尾。

    #include <deque>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int N=1e4+10;
    const int M=1e6+10;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m,s;
    int hd[N],w[M],to[M],nxt[M],tot;
    inline void add(int x,int y,int z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
    }
    deque<int>Q;
    int dis[N];
    bool vis[N];
    void spfa() {
    	for(int i=1;i<=n;i++)
    		dis[i]=0x7fffffff;
    	Q.push_back(s);
    	dis[s]=0;vis[s]=1;
    	while(Q.size()) {
    		int x=Q.front();Q.pop_front();
    		vis[x]=0;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y]>dis[x]+w[i]) {
    				dis[y]=dis[x]+w[i];
    				if(!vis[y]) {
    					vis[y]=1;
    					if(!Q.empty()&&dis[y]<dis[Q.front()]) Q.push_front(y);
    					else Q.push_back(y);
    				}
    			}
    		}
    	}
    }
    int main() {
    	n=read();m=read();s=read();
    	for(int i=1,x,y,z;i<=m;i++) {
    		x=read();y=read();z=read();
    		add(x,y,z);
    	}
    	spfa();
    	for(int i=1;i<=n;i++) 
    		printf("%d ",dis[i]);
    	return 0;
    }
    
    
    

    LLL

    对每个要出队的队头元素 (u),比较 (dist[u])和队列中点的 $dist $的平均值,如果 (dist[u]) 更大,将其弹出放到队尾,然后取队首元素进行相同操作,直到队头元素的 (dist)小于等于平均值。

    超级源点,超级会点

    Description

    为了提高服务器的耐受能力,很多流量大的网站都会架设多台服务器,而互联网的路由能找到线路最短的一台服务器。 现在UOI想要下片,他有好多台电脑,又有好多服务器可以提供下载。UOI将给你一个网络图,告诉你点点之间的线路长度,问最短的线路长是多少,以及选择的那台用来下载的电脑和被选的服务器的编号。 如果有多台电脑/服务器之间连线都是最短线路,输出电脑编号最小的;如果还有多种选择,输出服务器编号最小的。

    输入格式

    第一行n,m,表示总格有n个点,m条网络连线 接下来m行,表示每条网络连线所连接的A、B点和线的长度。 接下来一个数T1,表示UOI有多少台电脑。 下一行T1个数,表示UOI每台电脑的编号。 接下来一个数T2,表示有多少台服务器。 下一行T2个数,表示每台服务器编号。

    输出格式

    三个数,分别是线路长度,UOI下载用的电脑,提供片的下载源

    输入输出样例

    输入 #1复制

    5 3
    2 3 2
    1 4 2
    1 5 2
    2
    1 2
    2
    3 5
    

    输出 #1复制

    2 1 5
    

    说明/提示

    对于30%的数据,N<=100,M<=N^2 对于100%的测试数据,N<=100000,M<=400000,T1+T2<=N,其余数据在10^9以内,且大于0

    Solution

    这道题就是个最短路$spfa $

    很难想到的是建立超级源点和超级汇点;

    然后开个(find[])记录连接情况

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    #define re register int
    #define N 100005
    #define M 400005
    using namespace std;
    int t1,t2,s[N],fd[N];
    long long ans=0x7ffffff;
    struct edge{
        int v,w,nxt;
    }e[M<<1];
    int hd[N],cnt=0,n,m,dis[N];
    bool vis[N];
    queue<int>q;
    inline void add(int u,int v,int w){
        e[++cnt].v=v;
        e[cnt].w=w;
        e[cnt].nxt=hd[u];
        hd[u]=cnt;
    }
    
    int main(){
        memset(dis,0x3f,sizeof(dis));
        scanf("%d%d",&n,&m);
        for (re i=1;i<=m;i++) {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);
            add(y,x,z);
        }
        scanf("%d",&t1);
        for(re i=1;i<=t1;i++)
            scanf("%d",&s[i]);
        sort(s+1,s+1+t1);
        scanf("%d",&t2);
        int x;
        for(re i=1;i<=t2;i++){
            scanf("%d",&x);
            fd[x]=x;
            q.push(x);
            dis[x]=0;
            vis[x]=1;
        }
         while(!q.empty()){
            x=q.front();q.pop();vis[x]=0;
            for(int i=hd[x];i;i=e[i].nxt){
                int to=e[i].v;
                if(dis[to]>dis[x]+e[i].w){
                    dis[to]=dis[x]+e[i].w;
                    fd[to]=fd[x];
                    if(!vis[to]){
                        q.push(to);vis[to]=1;
                    }
                }
            }
        }
        int u;
        for(int i=1;i<=t1;i++)
            if(dis[s[i]]<ans)
                ans=dis[s[i]],u=s[i];
        printf("%lld %d %d",ans,u,fd[u]);
        return 0;
    }
    

    多源最短路(任意两点距离)

    Floyd O( (n^3) )

    for(re k=1;k<=n;k++)
        for(re i=1;i<=n;i++)
            for(re j=1;j<=n;j++)
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    

    注意循环的顺序

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
    int res=0;bool f=1;char c;c=getchar();
    	while(!isdigit(c)){if(c=='-')f=0;c=getchar();}
    	while(isdigit(c)){res=res*10+(c^48);c=getchar();}
    	return f?res:-res;
    }
    #define N 205
    #define inf 0x3f3f3f3f
    int n,m,q,k;
    int f[N][N],t[N];
    int main(){
    	n=read();m=read();
    	memset(f,0x3f,sizeof(f));
    	memset(t,0x3f,sizeof(t));
    	for(int i=0;i<n;i++)t[i]=read(),f[i][i]==0;
    	for(int i=0,x,y,z;i<m;i++){
    		x=read();y=read();z=read();
    		f[x][y]=f[y][x]=z;
    	}
    	q=read();
    	while(q--){
    		int x=read(),y=read(),z=read();
    		while(z>=t[k]){
    			for(int i=0;i<n;i++)
    				for(int j=0;j<n;j++)
    					if(f[i][j]>f[i][k]+f[k][j])
    						f[i][j]=f[i][k]+f[k][j];
    			k++;	
    		}
    		if(f[x][y]==inf || t[x]>z ||t[y]>z)puts("-1");
    		else printf("%d
    ",f[x][y]);
    	}
    	return 0;
    }
    
    

    传递闭包

    http://poj.org/problem?id=1094

    for(int i=1;i<=n;i++)
    	d[i][i]=1;
    for(int i=1;i<=m;i++){
    	x=read();y=read();
    	d[x][y]=d[y][x];
    }
    for(int k=1;k<=n;k++)
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			d[i][j]|=d[i][k]&d[k][j];
    

    Johnson 全源最短路

    先跑(spfa)解决负权问题,然后跑(n)(dij)

    #include<bits/stdc++.h>
    #define INF 1e9
    using namespace std;
    inline int read()
    {
        int x=0,k=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
        while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return x*k;
    }
    struct edge{
    	int v,w,next;
    }e[10005];
    struct node{
    	int dis,id;
    	bool operator<(const node&a)const{
      	return dis>a.dis;
    	}
    	node(int d,int x){
    		dis=d,id=x;
    	}
    };
    int head[5005],vis[5005],t[5005];
    int cnt,n,m;
    long long h[5005],dis[5005];
    void addedge(int u,int v,int w){
    	e[++cnt].v=v;
    	e[cnt].w=w;
    	e[cnt].next=head[u];
    	head[u]=cnt;
    }
    bool spfa(int s){
    	queue<int> q;
    	memset(h,63,sizeof(h));
    	h[s]=0,vis[s]=1;
    	q.push(s);
    	while(!q.empty()){
    		int u=q.front();
    		q.pop();
    		vis[u]=0;
    		for(int i=head[u];i;i=e[i].next){
    			int v=e[i].v;
    			if(h[v]>h[u]+e[i].w)
    			{
    				h[v]=h[u]+e[i].w;
    				if(!vis[v]){
    					vis[v]=1;
    					q.push(v);
    					t[v]++;
    					if(t[v]==n)return false;
    				}
    			}
    		}
    	}
    	return true;
    }
    void dijkstra(int s)
    {
    	priority_queue<node> q;
    	for(int i=1;i<=n;i++)
    		dis[i]=INF;
    	memset(vis,0,sizeof(vis));
    	dis[s]=0;
    	q.push(node(0,s));
    	while(!q.empty()){
    		int u=q.top().id;
    		q.pop();
    		if(vis[u])continue;
    		vis[u]=1;
    		for(int i=head[u];i;i=e[i].next){
    			int v=e[i].v;
    			if(dis[v]>dis[u]+e[i].w){
    				dis[v]=dis[u]+e[i].w;
    				if(!vis[v])q.push(node(dis[v],v));
    			}
    		}
    	}
    	return;
    }
    int main(){
    	n=read(),m=read();
    	for(int i=1;i<=m;i++){
    		int u,v,w;
    		u=read();v=read();w=read();
    		addedge(u,v,w);
    	}
    	for(int i=1;i<=n;i++)
    		addedge(0,i,0);
    	if(!spfa(0)){
    		cout<<-1<<endl;
    		return 0;
    	}
    	for(int u=1;u<=n;u++)
    		for(int i=head[u];i;i=e[i].next)
    			e[i].w+=h[u]-h[e[i].v];
    	for(int i=1;i<=n;i++){
    		dijkstra(i);
    		long long ans=0;
    		for(int j=1;j<=n;j++){
    			f(dis[j]==INF)ans+=j*INF;
    			else ans+=j*(dis[j]+h[j]-h[i]);
    		}
    	cout<<ans<<endl;
    	}
    	return 0;
    }
    

    分层图最短路

    例1:飞行路线

    重题

    一共建(k+1)层图。各层内部正常连边,各层之间从上到下连权值为(0)的边。每向下跑一层,就相当于免费搭一次飞机。跑一遍从(s)(t+n∗k)的最短路即可。(注意最后(ans)(0<=i<=k)最小)

    时间复杂度:(O(k*(m+n)log(n)))

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int N=10005;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m,k;
    int hd[N],to[N*10],tot,nxt[N*10],w[N*10];
    inline void add(int x,int y,int z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
    } 
    
    int s,t;
    int dis[N][12];
    
    struct node{
    	int u,d,dep;
    	node(){}
    	node(int uu,int dd,int dp){u=uu;d=dd;dep=dp;}
    	bool operator < (const node &x) const {
    		return d>x.d;
    	}
    };
    bool vis[N][12];
    priority_queue<node>q;
    void dij() {
    	memset(dis,0x3f,sizeof(dis));
    	q.push(node(s,0,0));
    	dis[s][0]=0;
    	while(q.size()) {
    		int x=q.top().u,dep=q.top().dep; q.pop();
    		if(vis[x][dep]) continue;
    		vis[x][dep]=1;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y][dep]>dis[x][dep]+w[i]) {
    				dis[y][dep]=dis[x][dep]+w[i];
    				if(!vis[y][dep]) q.push(node(y,dis[y][dep],dep));
    			}
    			if(dep<k&&dis[y][dep+1]>dis[x][dep]) {
    				dis[y][dep+1]=dis[x][dep];
    				if(!vis[y][dep+1]) q.push(node(y,dis[y][dep+1],dep+1));
    			}
    		}
    	}
    }
    int main() {
    	n=read();m=read();k=read();
    	s=read()+1;t=read()+1;
    	for(int i=1;i<=m;i++) {
    		int a=read()+1,b=read()+1,c=read();
    		add(a,b,c);add(b,a,c);
    	}
    	
    	dij();
    	int ans=0x3f3f3f3f;
    	for(int i=0;i<=k;i++)
    		ans=min(ans,dis[t][i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    例2:冻结

    #include<bits/stdc++.h>
    using namespace std;
    #define M 500005
    int hd[M<<1],to[M<<1],nxt[M<<1],w[M<<1];
    int n,m,k,tot;
    void add(int x,int y,int z){
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
    }
    struct node{
    	int u,val,dep; 	
    	node(){}
    	node(int a,int b,int c):u(a),dep(b),val(c){}
    	bool operator < (const node &x)const{
    		return val>x.val;
    	}
    };
    priority_queue< node > Q;
    int dis[M][55];
    bool vis[M][55];
    
    void dij(){
    	memset(dis,0x3f,sizeof(dis));
    	dis[1][0]=0;
    	Q.push(node(1,0,0));
    	while(Q.size()){
    		int dep=Q.top().dep,x=Q.top().u;
    		Q.pop();
    		if(vis[x][dep])continue;
    		vis[x][dep]=1;
    		for(int i=hd[x];i;i=nxt[i]){
    			int y=to[i];
    			if(dis[x][dep]+w[i]<dis[y][dep]){
    				dis[y][dep]=dis[x][dep]+w[i];
    				Q.push(node(y,dep,dis[y][dep]));
    			}
    			if(dep<=k&&dis[x][dep]+w[i]/2<dis[y][dep+1]){
    				dis[y][dep+1]=dis[x][dep]+w[i]/2;
    				Q.push(node(y,dep+1,dis[y][dep+1]));
    			}
    		}
    	}
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&k);
    	for(int i=1,x,y,z;i<=m;i++){
    		scanf("%d%d%d",&x,&y,&z);
    		add(x,y,z);
    		add(y,x,z);
    	}
    	dij();
    	int ans=0x3f3f3f3f;
    	for(int i=0;i<=k;i++)ans=min(ans,dis[n][i]);
    	printf("%d
    ",ans);
    	return 0;
    } 
    

    最短路计数

    在求最短路的同时维护(num)数组即可,这里特殊处理距离相等的情况

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int N=1000005;
    const int mod=100003;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m;
    int hd[N],to[N<<2],tot,nxt[N<<2];
    inline void add(int x,int y) {
    	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
    } 
    
    int dis[N],num[N];
    
    struct node{
    	int u,d;
    	node(){}
    	node(int uu,int dd){u=uu;d=dd;}
    	bool operator < (const node &x) const {
    		return d>x.d;
    	}
    };
    
    bool vis[N];
    priority_queue<node>q;
    
    void dij() {
    	memset(dis,0x3f,sizeof(dis));
    	q.push(node(1,0));
    	dis[1]=0;
    	num[1]=1;
    	while(q.size()) {
    		int x=q.top().u; q.pop();
    		if(vis[x]) continue;
    		vis[x]=1;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y]>dis[x]+1) {
    				dis[y]=dis[x]+1; 
    				num[y]=num[x];
    				if(!vis[y]) q.push(node(y,dis[y]));
    			}
    			else if(dis[y]==dis[x]+1) {
    				num[y]+=num[x];num[y]%=mod;
    				if(!vis[y]) q.push(node(y,dis[y]));
    			}
    		}
    	}
    }
    int main() {
    	n=read();m=read();
    	for(int i=1;i<=m;i++) {
    		int a=read(),b=read();
    		add(a,b);add(b,a);
    	}
    	
    	dij();
    	for(int i=1;i<=n;i++)
    		printf("%d
    ",num[i]);	
    	return 0;
    }
    

    Telephone Lines S

    求出(1)(n)的路径中第(k+1)大的边权尽量小

    由于答案具有单调性,我们二分花费,把(w>mid)的看成(1)(w<=mid)的看成(0),然后求 (1—n)的最短路,不超过(k)即合法

    这里用到双端(bfs)((0)扔前面,(1)扔后面)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <deque>
    using namespace std;
    const int N=20005;
    inline int read(){
    	int x=0;char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    	return x;
    }
    int n,m,k;
    int hd[N<<1],tot=0;
    struct edge{
    	int to,nxt,w;
    }e[N<<1];
    inline void add(int x,int y,int z){
    	e[++tot].to=y;e[tot].w=z;e[tot].nxt=hd[x];hd[x]=tot;
    }
    
    int dis[N];
    bool vis[N];
    bool check(int mid){
    	deque <int> q;
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[1]=0;q.push_front(1);
    	while(!q.empty()){
    		int x=q.front();q.pop_front();
    		if(vis[x]) continue;
    		vis[x]=1;
            for(int i=hd[x];i;i=e[i].nxt){
                int y=e[i].to,z=e[i].w>mid?1:0;
                if(dis[y]>dis[x]+z){
                    dis[y]=dis[x]+z;
                    if(!vis[y]){
                        if(!z) q.push_front(y);
                        else q.push_back(y);
                    }
                }
            }
    	}
    	if(dis[n]>k) return 0;
    	else return 1;
    }
    
    int mi=0x3f3f3f3f,mx;
    int main(){
    	n=read();m=read();k=read();
    	for(int i=1,a,b,c;i<=m;i++){
    		a=read();b=read();c=read();
    		add(a,b,c);add(b,a,c);
    		mx=max(mx,c);
    	}
    	int l=1,r=mx,ans=-1;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(check(mid)) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
    

    (spfa)版本

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N=20005;
    inline int read(){
    	int x=0;char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    	return x;
    }
    int n,m,k;
    int hd[N<<1],tot=0;
    struct edge{
    	int to,nxt,w;
    }e[N<<1];
    inline void add(int x,int y,int z){
    	e[++tot].to=y;e[tot].w=z;e[tot].nxt=hd[x];hd[x]=tot;
    }
    int dis[N];
    bool vis[N];
    queue<int>q;
    bool check(int mid){
    	for(int i=1;i<=n;i++) {
        	dis[i]=0x3f3f3f3f; 
        	vis[i]=0; 
        }
        q.push(1); dis[1]=0; vis[1]=1; 
        while(!q.empty()){
    	    int u=q.front(); 
    	    q.pop(); vis[u]=0; 
    	    for(int i=hd[u];i;i=e[i].nxt){
    	    	int v=e[i].to,z=e[i].w>mid?1:0; 
    	        if(dis[v]>dis[u]+z){
    				dis[v]=dis[u]+z;
    				if(!vis[v]) {
    					vis[v]=1; 
    					q.push(v);
    				}
    	        }
            }
        }
    	if(dis[n]>k) return 0;
    	else return 1;
    }
    
    int mi=0x3f3f3f3f,mx;
    int main(){
    	n=read();m=read();k=read();
    	for(int i=1,a,b,c;i<=m;i++){
    		a=read();b=read();c=read();
    		add(a,b,c);add(b,a,c);
    		mx=max(mx,c);
    	}
    	int l=1,r=mx,ans=-1;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(check(mid)) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
    

    最优贸易

    建反图的思想

    正着跑个从(1)(x)的最小值

    反着跑个从(1)(反图是(n))到(x)的最大值

    每个节点做差

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <iostream>
    using namespace std;
    const int N=100005;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m;
    int hd[N],val[N];
    int to[N<<2],tot,nxt[N<<2],w[N<<2];
    inline void add(int x,int y,int z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
    } 
    vector< pair<int,int> >G[N];
    #define MP make_pair
    inline void add2(int x,int y,int z){
    	
    }
    bool vis[N];
    int dis[N],disf[N];
    queue<int>q;
    void spfa(){                          
        q.push(1);     
        while(!q.empty()) {
            int x=q.front();q.pop();
            vis[x]=0;
            for(int i=hd[x];i;i=nxt[i]) {
                int y=to[i];    
                if(dis[y]>min(dis[x],w[i])) {
                   dis[y]=min(dis[x],w[i]);    
                   if(!vis[y]) q.push(y),vis[y]=1;
                }
            }
        }
    }
    void spfa_fan(){
        q.push(n);
        while(!q.empty()) {
            int x=q.front();q.pop();
            vis[x]=0;
            for(unsigned int i=0;i<G[x].size();i++) {
                int y=G[x][i].first,z=G[x][i].second;    
                if(disf[y]<max(disf[x],z)) {
                   disf[y]=max(disf[x],z);    
                   if(!vis[y]) q.push(y),vis[y]=1;
                }
            }
        }
    }
    int main(){
        n=read();m=read();
        for(int i=1;i<=n;i++) val[i]=read();
        for(int i=1;i<=m;i++){
            int x=read(),y=read(),z=read();
    		add(x,y,val[y]);
    		G[y].push_back(MP(x,val[x]));
            if(z==2) {
                add(y,x,val[x]);
                G[x].push_back(MP(y,val[y]));
            }
        }
    	memset(dis,0x3f,sizeof(dis));
        spfa();
    	memset(vis,0,sizeof(vis));
    	memset(disf,0,sizeof(disf));
        spfa_fan();
        int ans=0;
        for(int i=1;i<=n;i++)
            ans=max(ans,disf[i]-dis[i]);
        printf("%d",ans);
        return 0;
    }
    
    

    道路与航线

    速度限制 最短路变形+输出边

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    const int N=30005;
    int n,m,d;
    int head[N],vis[N][505],to[N],V[N],L[N],cnt,nxt[N],prx[N][505],prv[N][505]; 
    double z[N][505];
    void qxx(int x,int y,int v,int l){
        cnt++;nxt[cnt]=head[x];head[x]=cnt;to[cnt]=y;V[cnt]=v;L[cnt]=l;
    }
    struct node{
        int pos,val;
        node(int P,int VAL){
            pos=P;val=VAL;
        }
    };
    void print(int x,int y){
        if(x!=1)print(prx[x][y],prv[x][y]);
        printf("%d ",x-1);
    }
    void spfa(){
        memset(z,66,sizeof(z));
        queue<node>q;
        vis[1][70]=1;
        z[1][70]=0;
        q.push(node(1,70));
        while(!q.empty()){
            node k=q.front();q.pop();vis[k.pos][k.val]=0;
            for(register int i=head[k.pos];i;i=nxt[i]){
                int t=to[i];
                if(!V[i]){
                    if(z[k.pos][k.val]+1.0*L[i]/k.val<z[t][k.val]){
                        z[t][k.val]=z[k.pos][k.val]+1.0*L[i]/k.val;
                        prx[t][k.val]=k.pos;
                        prv[t][k.val]=k.val;
                        if(!vis[t][k.val]){
                            q.push(node(t,k.val));
                            vis[t][k.val]=1;
                        }
                    }
                }
                else{
                    int ms=V[i];
                    if(z[k.pos][k.val]+1.0*L[i]/ms<z[t][ms]){
                        z[t][ms]=z[k.pos][k.val]+1.0*L[i]/ms;
                        prx[t][ms]=k.pos;
                        prv[t][ms]=k.val;
                        if(!vis[t][ms]){
                            q.push(node(t,ms));
                            vis[t][ms]=1;
                        }
                    }
                }
            }
        }
        double minv=1<<30;int ans=0;
        for(register int i=1;i<=500;i++){
            if(minv>z[d][i]){
                minv=z[d][i];ans=i;
            }
        }
        print(prx[d][ans],prv[d][ans]);
    }
    int main(){
        n=read();m=read();d=read();d++;
        for(register int i=1;i<=m;i++){
            int x=read()+1,y=read()+1,v=read(),l=read();
            qxx(x,y,v,l);
        }
        spfa();
        printf("%d",d-1);
        return 0; 
    }
    

    [bzoj 4398] 福慧双修

    求从1号点出发,经过至少另一个点,走的边不重复的最小简单环。

    发现对于简单环上和1号点相接的两个点的二进制表示上一定有至少一位不一样。

    我们就把它二进制分组,然后在dij的时候就可以看看当前的to如果是1的话现在的点如果是分在出发点区域的就不行。

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    using namespace std;
    const int N=40005;
    struct Node {
        int id,dis;
    	bool operator < (const Node &rhs) const {
    		return dis>rhs.dis;}
    };
    priority_queue<Node>q;
    int n,m,dis[N],ans,head[N],ecnt;
    struct Edge{int to,nxt,val;}e[N*5];
    void add(int bg,int ed,int val) {e[++ecnt]=(Edge){ed,head[bg],val};head[bg]=ecnt;}
    void dij(int a,int b) {
        memset(dis,0x3f,sizeof dis);
        for(int i=head[1];i;i=e[i].nxt) 
            if((e[i].to&a)==b) 
    			q.push((Node){e[i].to,e[i].val}),dis[e[i].to]=e[i].val;
        while(!q.empty()) {
            Node u=q.top();q.pop();
            if(u.dis!=dis[u.id]) continue;
            for(int i=head[u.id];i;i=e[i].nxt) {
                if(e[i].to==1) {
                    if((u.id&a)!=b)
    					ans=min(ans,dis[u.id]+e[i].val); 
                    continue;
                }
                if(dis[u.id]+e[i].val<dis[e[i].to]) 
    				dis[e[i].to]=dis[u.id]+e[i].val,
    				q.push((Node){e[i].to,dis[e[i].to]});
            }
        }
    }
    int main() {
        scanf("%d%d",&n,&m);
        for(int i=1,a,b,c,d;i<=m;i++) 
            scanf("%d%d%d%d",&a,&b,&c,&d),add(a,b,c),add(b,a,d);
        ans=0x3f3f3f3f;
        for(int i=1;i<=n;i<<=1) {
            dij(i,i);
            dij(i,0);
        }
        cout<<ans;
        return 0;
    }
    
    
  • 相关阅读:
    二叉树的建树,按层遍历,结点总数,页结点,深度以及三序非递归遍历二叉树,建立中序线索二叉树
    志愿者选拔(单调队列)
    重建二叉树(中后序求前序)
    New Year Table(几何)
    Hopscotch(细节)
    红黑树(中序二叉树)
    Linux-awk命令详解
    Python--常用模块
    Python--re模块
    Python--模块与包
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13793993.html
Copyright © 2011-2022 走看看