【19.03.03】Codeforces上四道_最短路_相关题

T1_Mike and Shortcuts(BFS)

在这里插入图片描述
在这里插入图片描述
——题意——
每个点可以走向它前面或者后面的点,以及它所指向的捷径(一个点只有一个),路径长度都是1,要求输出从结点1走到其它所有点的最短路径长度。
——题解——
水题,BFS



——Code——

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int inf=0x7fffffff;
int dis[200005];
vector<int> edge[200005];
queue<int> q;
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;++i)
    {
        int to;
        cin>>to;
        edge[i].push_back(to);
        if(i>1) edge[i].push_back(i-1);
        if(i<n) edge[i].push_back(i+1);
    }

    for(int i=1;i<=n;++i)
        dis[i]=inf;
    dis[1]=0;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<edge[u].size();++i)
        {
            int v=edge[u][i];
            if(dis[v]==inf)
            {
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }
    for(int i=1;i<=n;++i)
        cout<<dis[i]<<" ";
    return 0;
}




T2_The Two Routes (BFS)

在这里插入图片描述
在这里插入图片描述
——题意——
有公交车和火车两种交通载具,火车和公交车不能再同一时刻在同一个城镇(除了起点1和终点n),题目给出城镇数 n n n和铁轨的连接情况,其余所有边都是公路,且所有铁轨和公路的长度都是1且双向可行。
——题解——
水题,两种载具中必有一种能直接走到终点,对另一种载具进行BFS即可。



——Code——

#include<iostream>
#include<queue>
using namespace std;
int edge[401][401];
int n,m;
int dis[401];
queue<int> q;
int main()
{
	cin>>n>>m;
	bool flag=false;
	for(int i=1;i<=m;++i)
	{
		int fr,to;
		cin>>fr>>to;
		edge[fr][to]=edge[to][fr]=1;
		if((fr==n&&to==1)||(fr==1&&to==n)) flag=true;
	}
	if(flag)
	{
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				if(i!=j)
					edge[i][j]=1-edge[i][j];
	}
	for(int i=1;i<=n;++i)
		dis[i]=-1;
	q.push(1);
	dis[1]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=1;i<=n;++i)
			if(edge[u][i]==1&&dis[i]==-1)
			{
				dis[i]=dis[u]+1;
				q.push(i);
			}
	}
	cout<<dis[n]<<endl;
	return 0;	
 } 




T3_Missile Silos (dijkstra)

在这里插入图片描述
在这里插入图片描述
——题意——
首先给出一张有权图,给出首都位置,从离首都距离为 l l l的位置有导弹仓库(可能在点上也可能在边上),求出导弹仓库的数量。
——题解——
本题的关键在于导弹仓库可能在路上,但有一点是确定的,到某条路上的导弹仓库的最近的路,一定由这条路两端的某个点的最短路径加上一定的值( l − d i s [ u ] l-dis[u] ldis[u])得到。先跑一边dijkstra,得出所有点的最短路径。并且把最短路径小于等于 l l l的点放进队列。最短路径等于 l l l的点,其实可以直接记下答案。对剩下的遍历。
设某结点的最短路为 d i s dis dis,对该结点的边进行遍历,边的长度为 w w w,排除以下几种情况:
d i s + w &lt; = l dis+w&lt;=l dis+w<=l,显然最短路等于 l l l的点都已经记录了,并且小于 l l l代表这条路上没有导弹,所以遍历下一条边。
②设这条边两边分别是 u u u v v v,如果 d i s [ u ] + w + d i s [ v ] &gt; 2 ∗ l dis[u]+w+dis[v]&gt;2*l dis[u]+w+dis[v]>2l,那么也是不符合的。这个表达式的意思就是,从这条边另一个端点走到预设的导弹仓库的位置比 l l l更短。
排除完上面那些情况,还有一种需要特殊判断:如果某条路的两个端点延伸出来的路径刚好在同一个位置找到导弹仓库,则此时答案会被多记一次。对这个特殊情况进行计数,最后减去其一半。



——Code——

#include<iostream>
#include<vector>
#include<queue>
#include<utility>
using namespace std;

const int inf=0x7fffffff;
typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,greater<pii> > q;
int dis[200005];
vector<pii> edge[200005];
int n,m,s;

void Dijkstra(int st)
{
	for(int i=0;i<=n;++i)
		dis[i]=inf;
	dis[st]=0;
	q.push(make_pair(0,st));
	while(!q.empty())
	{
		pii u=q.top();
		q.pop();
		if(u.first>dis[u.second]) continue;
		for(int i=0;i<edge[u.second].size();++i)
		{
			int v=edge[u.second][i].first;
			int weight=edge[u.second][i].second;
			if(dis[v]>dis[u.second]+weight)
			{
				dis[v]=dis[u.second]+weight;
				q.push(make_pair(dis[v],v));
			}
		}
	}
}
int main()
{
	cin>>n>>m>>s;
	for(int i=1;i<=m;++i)
	{
		int fr,to,weight;
		pii in;
		cin>>fr>>to>>in.second;
		in.first=to;
		edge[fr].push_back(in);
		in.first=fr;
		edge[to].push_back(in);
	}
	int l;
	cin>>l; 
	Dijkstra(s);
	for(int i=1;i<=n;++i)
		if(dis[i]<=l)
			q.push(make_pair(dis[i],i));
	int cnt=0;
	int ans=0;
	while(!q.empty())
	{
		pii node=q.top();
		q.pop();
		int u=node.second;
		if(dis[u]==l)
		{
			++ans;
			continue;
		}
		for(int i=0;i<edge[u].size();++i)
		{
			int v=edge[u][i].first;
			int w=edge[u][i].second;
			if(dis[u]+w<=l) continue;
			if(dis[v]+w+dis[u]<2*l) continue;
			if(dis[v]+w+dis[u]==2*l) ++cnt;
			++ans;
		}
	}
	cout<<ans-cnt/2<<endl;
}

T4_Paths and Trees(dijkstra)

在这里插入图片描述
在这里插入图片描述
——题意——
以u为起点到各个点的最短路,同时满足选取的边的权重和最小,输出最小权重和以及选取的边。
题目保证是连通图。

——题解——
n n n个点连接起来需要 n − 1 n-1 n1条边。
首先一定要满足这是最短路,在最短路的基础上要求权重和最小。
假设有两条通往结点 k k k的最短路径
在这里插入图片描述
显然我们选择绿色的这条路径,这样 k k k以前的所有点的权重和就会比选择另一条路径少 2 2 2.
本题关键在于,如何贪心的选取最短的边并且构成最短路径。在最短路径已经确定的情况下,我们希望能有较长的边被重复利用,如上图,选择绿色路径被重复利用路径的长度高于选另一条路径。等同于这一原则的条件即从 k k k的上一个结点选取到达 k k k的最短路时,选择一条通往 k k k的权重最小的边。首先,这样不会破坏该路径仍然为到 k k k的最短路的性质;其次,也不会影响后面的结点的最短路长度。从原点开始递推,每一步都选择最优解,最终也一定会得到最小的权重和。
所以,该题本质上是一个魔改的dijkstra算法。


#include<iostream>
#include<vector>
#include<queue>
#include<utility>
using namespace std;

const long long inf=999999999999999;
struct EDGE{
	int v;
	long long w;
	int num;
};
int re_id[300005];
int n,m;
long long dis[300005];
typedef pair<long long,int> plli;
vector<EDGE> e[300005];
long long we[300005];
priority_queue<plli,vector<plli>,greater<plli> > q;

void Dijkstra(int st)
{
	for(int i=1;i<=n;++i)
		dis[i]=inf;
	dis[st]=0;
	q.push(make_pair(0,st));
	while(!q.empty())
	{
		plli u=q.top();
		q.pop();
		if(dis[u.second]<u.first) continue;
		for(int i=0;i<e[u.second].size();++i)
		{
			int fr=u.second;
			int to=e[fr][i].v;
			long long weight=e[fr][i].w;
			int id=e[fr][i].num;
			if(dis[fr]+weight<dis[to])
			{
				dis[to]=dis[fr]+weight;
				q.push(make_pair(dis[to],to));
				re_id[to]=id;
			}
			else if(dis[fr]+weight==dis[to]&&we[re_id[to]]>weight)
				re_id[to]=id;
		}
	}
}
int main()
{
	int st;
	cin>>n>>m;
	for(int i=1;i<=m;++i)
	{
		int fr,to;
		EDGE in;
		cin>>fr>>to>>in.w;
		in.num=i;
		in.v=to;
		e[fr].push_back(in);
		in.v=fr;
		e[to].push_back(in);
		we[i]=in.w;
	}
	cin>>st;
	Dijkstra(st);
	long long ans=0;
	for(int i=1;i<=n;++i)
		if(i!=st) ans+=we[re_id[i]];
	cout<<ans<<endl;
	for(int i=1;i<=n;++i)
		if(i!=st) cout<<re_id[i]<<" ";
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值