最短路

最短路

单源最短路

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;
}

原文地址:https://www.cnblogs.com/ke-xin/p/13793993.html