有关最短路上的第k小/大值的总结

1.USACO08JAN  Telephone Lines 题面

由于问的是最大值最小,所以二分加验证就好了

比较显然的,题干问的是第k+1长的路最短;

那么二分答案是正确的方向;

但是怎么验证?

我们可以将所有边权大于二分的答案的边视为边权是1,否则看成0;

然后从1~n跑最短路,如果答案大于二分的答案那么就不成立,否则成立;

这种思维比较重要,代码还是很简单的;

#include <bits/stdc++.h>
using namespace std;
int head[2000010],cnt;
class littlestar{
	public:
		int to;
		int nxt;
		int w;
		void add(int u,int v,int gg){
			nxt=head[u];
			to=v;
			w=gg;
			head[u]=cnt;
		}
}star[2000010];
int n,p,k;
int dis[100010],vis[100010];
bool SPFA(int x)
{
	queue<int> q;
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0;
	memset(vis,0,sizeof(vis));
	q.push(1);
	while(q.size()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=star[i].nxt){
			int v=star[i].to;
			if(dis[v]>dis[u]+(star[i].w>x)){
				dis[v]=dis[u]+(star[i].w>x);
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
	if(dis[n]<=k) return 1;
	else{
		return 0;
	} 
}
int main()
{
	cin>>n>>p>>k;
	for(int i=1;i<=p;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		star[++cnt].add(u,v,w);
		star[++cnt].add(v,u,w);
	}
	int l=0,r=1000000;
	while(l<r){
		int mid=(l+r)/2;
		if(SPFA(mid)){
			r=mid;
		}
		else{
			l=mid+1;
		}
	}
	if(l==1000000) l=-1;
	cout<<l<<endl;
}

 2.[JLOI2011]飞行路线题面

这道题很上一道题不太一样,所求的是对于每种方案去掉前k大边后的最短路;

首先由于k很小,可以进行分层图;

第i层图表示目前已经删去了i条边的最短路;

对于点对(i,j)在任意层中边权是w;从第i层到第i+1层的边权是0;

然后堆优化+dijkstra就可以了;

#include <bits/stdc++.h>
using namespace std;
int head[5000010],cnt;
class littlestar{
	public:
		int to;
		int nxt;
		int w;
		void add(int u,int v,int gg){
			nxt=head[u];
			to=v;
			w=gg;
			head[u]=cnt;
		}
}star[5000010];
int n,p,k;
int dis[5000010],vis[5000010];
int S,T;
void dijkstra()
{
	memset(dis,0x3f,sizeof(dis));
	priority_queue<pair<int,int> > q;
	q.push(make_pair(0,S));
	dis[S]=0;
	while(q.size()){
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=star[i].nxt){
			int v=star[i].to;
			if(dis[v]>dis[u]+star[i].w){
				dis[v]=dis[u]+star[i].w;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
}
int main()
{
	cin>>n>>p>>k;
	cin>>S>>T;
	++S;++T;
	for(int i=1;i<=p;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		++u; ++v;
		star[++cnt].add(u,v,w);
		star[++cnt].add(v,u,w);
		for(int j=0;j<k;j++){
			star[++cnt].add((j+1)*n+u,(j+1)*n+v,w);
			star[++cnt].add((j+1)*n+v,(j+1)*n+u,w);
			star[++cnt].add(j*n+u,(j+1)*n+v,0);
			star[++cnt].add(j*n+v,(j+1)*n+u,0);
		}
	}
	for(int j=0;j<=k;j++){
		star[++cnt].add(j*n+T,n*30+T,0);
	}
	dijkstra();
	cout<<dis[n*30+T];
}

 3.[BJWC2012]冻结

和上一道题思路一样,都是分层图,但连接第i层和第i+1层的边权不再是0,而是w/2;

#include <bits/stdc++.h>
using namespace std;
int head[5000010],cnt;
class littlestar{
	public:
		int to;
		int nxt;
		int w;
		void add(int u,int v,int gg){
			nxt=head[u];
			to=v;
			w=gg;
			head[u]=cnt;
		}
}star[5000010];
int n,p,k;
int dis[5000010],vis[5000010];
void dijkstra()
{
	memset(dis,0x3f,sizeof(dis));
	priority_queue<pair<int,int> > q;
	q.push(make_pair(0,1));
	dis[1]=0;
	while(q.size()){
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=star[i].nxt){
			int v=star[i].to;
			if(dis[v]>dis[u]+star[i].w){
				dis[v]=dis[u]+star[i].w;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
}
int main()
{
	cin>>n>>p>>k;
	for(int i=1;i<=p;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		star[++cnt].add(u,v,w);
		star[++cnt].add(v,u,w);
		for(int j=0;j<k;j++){
			star[++cnt].add((j+1)*n+u,(j+1)*n+v,w);
			star[++cnt].add((j+1)*n+v,(j+1)*n+u,w);
			star[++cnt].add(j*n+u,(j+1)*n+v,w/2);
			star[++cnt].add(j*n+v,(j+1)*n+u,w/2);
		}
	}
	for(int j=0;j<=k;j++){
		star[++cnt].add(j*n+n,n*30+1,0);
	}
	dijkstra();
	cout<<dis[n*30+1];
}

 4.[USACO09FEB]改造路Revamping Trails

思路重复,代码几乎一样,就不多说了;把这到题放到这里的原因是想告诉大家,USACO的题要好好做啊,很多省选题都来源于此;

#include <bits/stdc++.h>
using namespace std;
int head[5000010],cnt;
class littlestar{
	public:
		int to;
		int nxt;
		int w;
		void add(int u,int v,int gg){
			nxt=head[u];
			to=v;
			w=gg;
			head[u]=cnt;
		}
}star[5000010];
int n,p,k;
int dis[5000010],vis[5000010];
void dijkstra()
{
	memset(dis,0x3f,sizeof(dis));
	priority_queue<pair<int,int> > q;
	q.push(make_pair(0,1));
	dis[1]=0;
	while(q.size()){
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=star[i].nxt){
			int v=star[i].to;
			if(dis[v]>dis[u]+star[i].w){
				dis[v]=dis[u]+star[i].w;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
}
int main()
{
	cin>>n>>p>>k;
	for(int i=1;i<=p;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		star[++cnt].add(u,v,w);
		star[++cnt].add(v,u,w);
		for(int j=0;j<k;j++){
			star[++cnt].add((j+1)*n+u,(j+1)*n+v,w);
			star[++cnt].add((j+1)*n+v,(j+1)*n+u,w);
			star[++cnt].add(j*n+u,(j+1)*n+v,0);
			star[++cnt].add(j*n+v,(j+1)*n+u,0);
		}
	}
	for(int j=0;j<=k;j++){
		star[++cnt].add(j*n+n,n*30+1,0);
	}
	dijkstra();
	cout<<dis[n*30+1];
}

 

posted @ 2019-10-10 19:32  神之右大臣  阅读(443)  评论(0编辑  收藏  举报