『毒瘤算法系列4』益智游戏(最短路图·拓扑最长链)

P r o b l e m \mathrm{Problem} Problem

小P和小R在玩一款益智游戏。游戏在一个正权有向图上进行。
小P控制的角色要从 A A A点走最短路到 B B B点,小R控制的角色要从 C C C点走最短路到 D D D点。
一个玩家每回合可以有两种选择,移动到一个相邻节点或者休息一回合。
假如在某一时刻,小P和小R在相同的节点上,那么可以得到一次特殊奖励,但是在每个节点上最多只能得到一次。
求小P和小R最多能获得多少次特殊奖励。

S o l u t i o n \mathrm{Solution} Solution

有一个比较显然的结论就是:

  • 两者的共同路径一定是连续的。
  • 假设存在两段不连续的路径,则取两者之间的公共端点一定可以使答案更优。

那么我们就要找到两者最短路图的交集,求该交集DAG的最长链

一些细节:

  • 可以两条路径之间只有公共点没有公共边,所以需要特殊判断公共点并将f[公共点]=1.
  • 最后输出的答案不要边的最长链+1,因为最终的答案有可能出现答案为0的现象。

C o d e \mathrm{Code} Code

#include <bits/stdc++.h>

#define int long long

using namespace std;
const int N = 3e5;

int n, m, A, B, C, D, res(0);
int dis[6][N], vis[N], f[N], in[N];
vector < int > a[N];
vector < pair<int,int> > G1[N], G2[N];
struct node {
	int x, y, v;
} e[N];

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (c < '0' || c > '9') w |= c == '-', c = getchar();
	while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
	return w ? -s : s;
}

void Dijkstra(int k, vector < pair<int,int> > a[N], int S)
{
	memset(vis,0,sizeof vis);
	memset(dis[k],30,sizeof dis[k]);
	priority_queue < pair<int,int> > q;
	dis[k][S] = 0; q.push({0,S});
	while (q.size())
	{
		int x = q.top().second; q.pop();
		if (vis[x]) continue; vis[x] = 1;
		for (int i=0;i<a[x].size();++i)
		{
			int y = a[x][i].first;
			int v = a[x][i].second;
			if (dis[k][x] + v < dis[k][y]) {
				dis[k][y] = dis[k][x] + v;
				q.push({-dis[k][y],y});
			}
		}
	}
	return;
}

int Topsort(void)
{
	queue < int > q;
	for (int i=1;i<=n;++i)
	    if (in[i] == 0) q.push(i);
	while (q.size())
	{
		int x = q.front(); q.pop();
		for (int i=0;i<a[x].size();++i)
		{
			int y = a[x][i];
			in[y] --;
			f[y] = max(f[y],f[x]+1);
			res = max(res,f[y]);
			if (in[y] == 0) q.push(y);
		}
	}
	return res;
}

signed main(void)
{
	n = read(), m = read();
	for (int i=1,x,y,v;i<=m;++i)
	{
		x = read(), y = read(), v = read();
		G1[x].push_back({y,v});
		G2[y].push_back({x,v});
		e[i] = node{x,y,v};
	}
	A = read(), B = read();
	C = read(), D = read();
	Dijkstra(1,G1,A);
	Dijkstra(2,G2,B);
	Dijkstra(3,G1,C);
	Dijkstra(4,G2,D);
	if (dis[1][B] > 1e12 || dis[3][D] > 1e12)
		return puts("-1"), 0;
	for (int i=1;i<=n;++i)
		if (dis[1][i] + dis[2][i] == dis[1][B]
		 && dis[3][i] + dis[4][i] == dis[3][D]) 
		 	res = f[i] = 1;
	if (res == 0) return puts("0"), 0;
	for (int i=1,x,y,v;i<=m;++i) 
	{
		int tag = 0;
		x = e[i].x, y = e[i].y, v = e[i].v;
		if (dis[1][x] + dis[2][y] + v == dis[1][B] 
		&&  dis[3][x] + dis[4][y] + v == dis[3][D]) tag = 1;
		if (tag) a[x].push_back(y), in[y] ++;
	} 
	cout << Topsort() << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值