P3720 [AHOI2017初中组]guide

算法标签:最短路

难度:提高+/省选-

本题说的是John有两个GPS系统,给了n个点和m条边。唯一独特的是,两个GPS所给的路途时间不同,所以从点1到点n的最短路径不同。如果John从当点走的路线与两个GPS给的从当点到终点的最短路径不同,则GPS会抱怨(一个不同抱怨一下,两个的话两下),求最少抱怨总数。

怎么判断走这条边会抱怨几次呢?

我们无论怎么走,都是要从此点沿最短路径走到终点,不妨我们都用最短路径算法求出两个GPS从点1->n-1到点n的最短路径(就是以点n为源点,求到所有点的最短路径,反过来就是从点1->n-1到点n的最短路径了。这里要注意,反着求的时候,边的方向要反过来,否则求得的结果是错误的!!)

由于极限情况是100000个点,只能选择效率高的算法,例如SPFA,或者我写的dijkstra堆优化版。

求得两个GPS从前n-1个点到终点的最短路径后,判断走这条边,是否抱怨,方法不难:枚举每条边,沿着这条边走时,若原来该点的最短路径值 - 该边的权值(即时间)=下一点的最短路径时,说明该边是最短路径上的一条边,不会抱怨;若不等于,则说明该边不是最短路径上的边,该边抱怨次数+1。于是我们得到了每条边抱怨的次数。

最后再以抱怨次数为边的权值,求一下从起点到终点的最短路径,即为最少抱怨数。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define N 100010
#define M 500010
#define INF 1e12
#define LL long long
int n,m,cnt,h[N],v[N];
LL d[3][N];
struct Node
{
	int to;
	LL w[3];
}e;
vector <Node> g[N];
queue <int> q;

int read ()
{
	int s=0,k=1;char ch=getchar ();
	while (!isdigit (ch)) {if (ch=='-') k=-1;ch=getchar ();}
	while (isdigit (ch)) {s=s*10+ch-'0';ch=getchar ();}
	return k*s;
}

void Init ()
{
	n=read ();m=read ();
	for (int i=1;i<=m;i++)
	{
		int x=read (),y=read ();
		LL w1=read (),w2=read ();
		e.to=x;e.w[0]=w1;e.w[1]=w2;
		g[y].push_back (e);
	}
	for (int i=1;i<=n;i++)
		d[0][i]=d[1][i]=d[2][i]=INF;
}

void SPFA (int s,int mode)
{
	memset (v,0,sizeof (v));
	q.push (s);d[mode][s]=0;v[s]=1;
	while (!q.empty ())
	{
		int x=q.front ();
		q.pop ();v[x]=0;
		for (int i=0;i<g[x].size ();i++)
		{
			int y=g[x][i].to;
			LL w=g[x][i].w[mode];
			if (d[mode][x]+w<d[mode][y])
			{
				d[mode][y]=d[mode][x]+w;
				if (!v[y])
				{
					q.push (y);
					v[y]=1;
				}
			}
		}
	}
}

void Work ()
{
	SPFA (n,0);
	SPFA (n,1);
	for (int x=1;x<=n;x++)
		for (int i=0;i<g[x].size ();i++)
		{
			int y=g[x][i].to;
			LL w1=g[x][i].w[0],w2=g[x][i].w[1];
			if (d[0][y]!=d[0][x]+w1) g[x][i].w[2]++;
			if (d[1][y]!=d[1][x]+w2) g[x][i].w[2]++;
		}
	SPFA (n,2);
	printf ("%lld\n",d[2][1]);
}

int main ()
{
	Init ();
	Work ();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值