Icebound

icebound-area

【二刷POJ】最短路总结

感觉把POJ最短路都刷了刷,大概分了几类

1 裸体最短路
poj1847 注意题意 每个站对应的第一个位置是不用搬轨道的,其他的都是用搬一下
然后直接跑

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=105,M=N*N*2;
int head[N],list[M],nxt[M],len[M],tot;
int n,ss,tt;
void add(int a,int b,int c)
{
	tot++;
	list[tot]=b,len[tot]=c;
	nxt[tot]=head[a];
	head[a]=tot;
}
int dist[N],vis[N];
int spfa(int s,int t)
{
	queue<int>q;q.push(s);
	memset(dist,0x3f,sizeof dist);
	dist[s]=0;memset(vis,0,sizeof vis);
	while(!q.empty())
	{
		int v=q.front();q.pop();vis[v]=0;
		for(int i=head[v];i;i=nxt[i])
		{
			int u=list[i];
			if(dist[u]>dist[v]+len[i])
			{
				dist[u]=dist[v]+len[i];
				if(!vis[u])
				{
					vis[u]=1;q.push(u);
				}
			}
		}
	}
	return dist[t];
}
int main()
{
	scanf("%d%d%d",&n,&ss,&tt);
	for(int i=1;i< =n;i++)
	{
		int x;scanf("%d",&x);
		for(int j=1;j<=x;j++)
		{
			int t;scanf("%d",&t);
			if(j==1)add(i,t,0);
			else add(i,t,1);
		}
	}
	int ans=spfa(ss,tt);
	if(ans!=0x3f3f3f3f)cout<<ans<<endl;
	else puts("-1");
}

2 来回的最短路
poj 2387
要想知道去和回来(形成一个环)的最短路,跑一遍正向图,再跑一遍反向就行了。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1005,M=100005;
int head[N],nxt[M],list[M],len[M],tot;
int a[M],b[M],c[M];
bool vis[N];
int dist[N],d[N],n,m,ss;
void add(int a,int b,int c)
{
	tot++;
	list[tot]=b,len[tot]=c;
	nxt[tot]=head[a];
	head[a]=tot;
}
void spfa(int s)
{
	memset(dist,0x3f,sizeof dist);
	memset(vis,0,sizeof vis);
	dist[s]=0;
	queue<int>q;q.push(s);vis[s]=1; 
	while(!q.empty())
	{
		int v=q.front();q.pop();vis[v]=0;
		for(int i=head[v];i;i=nxt[i])
		{
			int u=list[i];
			if(dist[u]>dist[v]+len[i])
			{
				dist[u]=dist[v]+len[i];
				if(!vis[u])
				q.push(u),vis[u]=1;
			}
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&ss);
	for(int i=1;i< =m;i++)
	{
		scanf("%d%d%d",&a[i],&b[i],&c[i]);
		add(a[i],b[i],c[i]);
	}
	spfa(ss);
	for(int i=1;i<=n;i++)d[i]=dist[i]; 
	memset(head,0,sizeof head);tot=0;
	for(int i=1;i<=m;i++)add(b[i],a[i],c[i]);
	spfa(ss);int ans=0;
	for(int i=1;i<=n;i++)
	ans=max(ans,d[i]+dist[i]);
	printf("%d\n",ans);
}

3 路径上的最大/小值
这类问题一般都是求一条路径 要求在路径上某边的权值最大值最小 或最小值最大
以最小值最大为例
我们改变一下dist数组的定义:dist[i]定义为从原点到点i路径上权值最小值的最大值。
然后松弛条件改为dist[u]=max(dist[u],min(dist[v],len[i]));
于是跑dij或者spfa都可以了
poj1797 便是最大值最小
(spfa比dij慢一点,在这道题上)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N=3005,M=2*N*N+5;
int head[N],nxt[M],list[M],len[M],tot;
int dist[N],n,m;
void add(int a,int b,int c)
{
	tot++;
	list[tot]=b,len[tot]=c;
	nxt[tot]=head[a]; 
	head[a]=tot;
}
struct edge
{
	int d,x;
	edge(){}
	edge(int a,int b){d=a,x=b;}
	friend bool operator< (edge n1, edge n2)
	{
		return n1.d<n2.d;//从最大排到最小
	}
};
void dij()
{
	memset(dist,-0x3f,sizeof dist);
	priority_queue<edge>q;
	for(int i=head[1];i;i=nxt[i])
	q.push(edge(len[i],list[i])),dist[list[i]]=len[i];
	while(!q.empty())
	{
		edge now=q.top();q.pop();
		int v=now.x;
		if(now.d<dist [v])continue;
		for(int i=head[v];i;i=nxt[i])
		{
			int u=list[i];
			if(dist[u]<min(dist[v],len[i]))
			{
				dist[u]=min(dist[v],len[i]);
				q.push(edge(dist[u],u));
			}
		}
	}
}
int main()
{
	int t,cnt=0;scanf("%d",&t);
	while(t--)
	{ 
		scanf("%d%d",&n,&m);
		memset(head,0,sizeof head);tot=0;
		for(int i=1;i<=m;i++)
		{
			int a,b,c;scanf("%d%d%d",&a,&b,&c);
			add(a,b,c),add(b,a,c);
		}
		dij();
		printf("Scenario #%d:\n",++cnt);
		printf("%d\n",dist[n]);
		puts("");
	}
	return 0;
}

4 spfa判环
在spfa中,某一个点最多进队出队n-1次(bellman-ford算法,全松弛完毕执行n-1次循环)
倘若进队出队超过n-1次,说明出现了负(正)环。
poj2240 一个汇率问题
典型的判环,边上的权值乘一下dist即可完成松弛时的汇率计算
(记得把初始时的钱加多点)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<map>
#include<string>
using namespace std;
const int N=50,M=N*N*2;
map</string><string ,int>hash;
int head[N],nxt[M],list[M],tot;
int n,m;
double len[M];
void add(int a,int b,double c)
{
	tot++;
	list[tot]=b,len[tot]=c;
	nxt[tot]=head[a];
	head[a]=tot;
}
int use[N],vis[N];
double dist[N];
bool spfa()
{
	queue<int>q;
	memset(dist,0,sizeof dist);
	memset(vis,0,sizeof vis);
	memset(use,0,sizeof use);
	dist[1]=100000;
	q.push(1);use[1]++;
	while(!q.empty())
	{
		int v=q.front();q.pop();vis[v]=0;
		for(int i=head[v];i;i=nxt[i])
		{
			int u=list[i];
			if(dist[u]<dist [v]*len[i])
			{
				dist[u]=dist[v]*len[i];
				if(!vis[u])
				{
					vis[u]=1;q.push(u);use[u]++;
					if(use[u]>n-1)
					return 1;
				}
			}
		}
	}
	return 0;
}
int main()
{
	int cnt=0;
	while(1)
	{
		scanf("%d",&n);
		if(n==0)return 0;
		memset(head,0,sizeof head);tot=0;
		hash.clear();
		for(int i=1;i< =n;i++)
		{
			string t;cin>>t;
			hash[t]=i;
		}
		scanf("%d",&m);
		for(int i=1;i< =m;i++)
		{
			string t1,t2;double x;
			cin>>t1>>x>>t2;
			add(hash[t1],hash[t2],x);
		}
		printf("Case %d: ",++cnt);
		puts(spfa()?"Yes":"No");
	}
}
</dist></int></string></map></algorithm></queue></cstring></cstdio></iostream>

poj1860 另一个汇率问题
在这道题上我们耍了个小伎俩,只需要每次判断dist[1]是不是变大了即可
【滑稽】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1050;
const double eps=1e-7;
int head[N],nxt[N*N*2],list[N*N*2],tot;
double len[N*N*2],slen[N*N*2];
void add(int a,int b,double c,double d)
{
	tot++;
	list[tot]=b,len[tot]=c,slen[tot]=d;
	nxt[tot]=head[a];
	head[a]=tot;
}
bool vis[N];
int n,m,s;double t;
double dist[N];
bool spfa()
{
	queue<int>q;
	dist[s]=t;
	q.push(s);vis[s]=1;
	while(!q.empty())
	{
		int v=q.front();q.pop();vis[v]=0;
		for(int i=head[v];i;i=nxt[i])
		{
			int u=list[i];
			if(dist[u]< (dist[v]-slen[i])*len[i])
			{
				dist[u]=(dist[v]-slen[i])*len[i];
				if(!vis[u])
				q.push(u),vis[u]=1;
			}
			if(u==s&&dist[u]>t)return 1;
		}
	}
	return 0;
}
int main()
{
	cin>>n>>m>>s>>t;
	for(int i=1;i< =m;i++)
	{
		int a,b;
		double c,d,e,f;
		cin>>a>>b>>c>>d>>e>>f;
		add(a,b,c,d);
		add(b,a,e,f);
	}
	puts(spfa()?"YES":"NO");
}
</int></queue></algorithm></cstring></cstdio></iostream>

4 floyd传递闭包
floyd可以很快的完成闭包传递,但是最快的方法好像是bitset
这个很简单,就略过了,附上之前的某个链接
https://icebound.cc/2015/08/01/stdbitset.html
n^2的传递闭包↑

5 带条件的最短路
poj 1062 昂贵的聘礼
这可能属于带条件的最短路?
这题比较有意思 枚举可以交易的等级范围,再跑最短路,在最短路上严格限定刚刚枚举的等级
不过注意一个问题,枚举时记得注意细节!
细节细节细节!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=200,M=N*N*3;
int head[N],list[M],nxt[M],len[M],tot;
int m,n,le[N];
void add(int a,int b,int c)
{
	tot++;
	list[tot]=b,len[tot]=c;
	nxt[tot]=head[a];
	head[a]=tot;
}
int dist[N],vis[N];
int spfa(int l,int r)
{
	queue<int>q;q.push(0);
	memset(dist,0x3f,sizeof dist);
	dist[0]=0;memset(vis,0,sizeof vis);
	while(!q.empty())
	{
		int v=q.front();q.pop();vis[v]=0;
		for(int i=head[v];i;i=nxt[i])
		{
			int u=list[i];
			if(le[u]>=l&&le[u]< =r)
			{
				if(dist[u]>dist[v]+len[i])
				{
					dist[u]=dist[v]+len[i];
					if(!vis[u])
					{
						vis[u]=1;q.push(u);
					}
				}
			}
		}
	}
	return dist[1];
}
int main()
{
	scanf("%d%d",&m,&n);
	int ll=0x3f3f3f3f,rr=-0x3f3f3f3f;
	for(int i=1;i< =n;i++)
	{
		int x,y;scanf("%d%d%d",&x,&le[i],&y);
		ll=min(ll,le[i]),rr=max(rr,le[i]);
		add(0,i,x);
		for(int j=1;j<=y;j++)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			add(a,i,b);
		}
	}
	int ans=0x3f3f3f3f;
	for(int i=ll;i<=rr;i++)
	{
		int x=i+m;
		ans=min(ans,spfa(i,x));
		if(i+m>rr)break;
	}
	printf("%d\n",ans);
	return 0;
}
</int></queue></cstring></algorithm></cstdio></iostream>

6 关于最短路的补充
我现在对于dij spfa(bellman-ford)和floyd的理解又改变了
感觉dij像是一个变种bfs算法,spfa是一种对边进行枚举的最短路算法,floyd是一种dp
以后还会再更新吧?