P5590 赛车游戏

题目描述

R 君和小伙伴打算一起玩赛车。但他们被老司机 mocania 骗去了秋名山。

秋名山上有 n 个点和 m 条边,R 君和他的小伙伴要从点 1 出发开往点 n,每条边都有一个初始的方向。老司机 mocania 拿到了秋名山的地图但却不知道每条路有多长。显然,为了赛车游戏的公平,每条 11 到 nn 的路径应当是等长的。mocania 想,我就随便给边表上一个 1...9 的长度,反正傻傻的 R 君也看不出来。

可 mocania 的数学不大好,不知道怎么给边标长度,只能跑来请教你这个 OI 高手了。

输入格式

第一行两个整数 n,m。

接下来 mm 行,每行两个整数 u,v,表示一条从 u 到 v 的有向边。

输出格式

如果无解或者不存在 1 到 n 的路径直接输出一个 -1。

如果有解第一行输出两个数 n,m,和输入文件中给出的相同。

借下来 m 行,每行三个整数 u,v,w,表示把从 u 到 v 的路径的长度设置为 w,其中 ww 是一个 1...9 的整数。要求所有边的出现顺序和题目中给出的相同。

输入输出样例

输入 #1复制

10 10
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
1 10

输出 #1复制

10 10
1 2 1
2 3 1
3 4 1
4 5 1
5 6 1
6 7 1
7 8 1
8 9 1
9 10 1
1 10 9

说明/提示

数据范围

本题启用 Special Judge 和 Subtask。

Subtask #1:30% 的分数,n <= 10, m <= 20
Subtask #2:60% 的分数,n <= 100, m <= 200
Subtask #3:100% 的分数,n <= 1000, m <= 2000

扫描二维码关注公众号,回复: 10746075 查看本文章

保证数据中不会出现重边,自环。

这个是luogu10月月赛的题,当时被虐到只会puts("0")
现在。。。只会puts("-1")...

差分约束
题目要求每个边的长度都在1到9之间。也就是说对于一个边(u ,v) 有 1 <= dis[v] - dis[u] <= 9
我个人喜欢大连小,所以 连边(v , u , 1) , (u , v , -9)
注意 开始时dis不能赋成0,有负数。。。
还有要把不在1到n路径上的边忽略,不给他们连差分约束的边。。
这个可以用一正一反两边dfs搞定。。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 1010;
inline int read()
{
    register int x = 0 , f = 0; register char c = getchar();
    while(c < '0' || c > '9') f |= c == '-' , c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return f ? -x : x;
}
int n , m , cnt;
int head[N] , eu[N<<1] , ev[N<<1] , vis[N] , t[N] , dis[N] , bo[N] , bo1[N] , bo2[N];
struct edge{ int v , nex , c; }e[N<<2];
inline void add(int u , int v , int c) { e[++cnt].v = v; e[cnt].nex = head[u]; e[cnt].c = c; head[u] = cnt; return ; }

void dfs1(int x)
{
	bo1[x] = 1;
	for(int i = head[x]; i ; i = e[i].nex) if(i & 1) // 求出1能到那些点 
		if(!bo1[e[i].v]) dfs1(e[i].v);
	return ;
}

void dfs2(int x)
{
	bo2[x] = 1;
	for(int i = head[x] ; i ; i = e[i].nex) if(!(i & 1)) // 求出那些点能到n
		if(!bo2[e[i].v]) dfs2(e[i].v);
	return ;
}

queue<int> q;
bool SPFA()
{
	for(int i = 2 ; i <= n ; ++i) dis[i] = -1e8; q.push(1);
	while(q.size())
	{
		int x = q.front(); q.pop(); vis[x] = 0; if((++t[x]) >= n) return false; // 此处是 >= n , .. 不是n-1 , 反正写多点没错。 
		for(int i = head[x] , v; i ; i = e[i].nex)
		{
			v = e[i].v;
			if(dis[v] < dis[x] + e[i].c)
			{
				dis[v] = dis[x] + e[i].c;
				if(!vis[v]) vis[v] = 1 , q.push(v);
			}
		}
	}
	return true;
}

int main()
{
	n = read(); m = read();
	for(int i = 1 ; i <= m ; ++i) eu[i] = read() , ev[i] = read() , add(eu[i] , ev[i] , 0) , add(ev[i] , eu[i] , 0);
	dfs1(1); 
	if(!bo1[n]) { puts("-1"); return 0; } // 1 不能到 n 不连通 
	dfs2(n);
	for(int i = 1 ; i <= n ; ++i) if(bo1[i] && bo2[i]) bo[i] = 1; bo[1] = bo[n] = 1; // bo = 1 是有用点 
	memset(head , 0 , sizeof head); cnt = 0; // 重复利用 
	for(int i = 1 ; i <= m ; ++i)  // 建差分约束的边 
		if(bo[eu[i]] && bo[ev[i]]) add(eu[i] , ev[i] , -9) , add(ev[i] , eu[i] , 1);
	if(!SPFA()) { puts("-1"); return 0; } // 无解 
	cout << n << ' ' << m << '\n';
	for(int i = 1 ; i <= m ; ++i) // 输出是判一下是不是在1到n的路径上 
		cout << eu[i] << ' ' << ev[i] << ' ' << ((bo[eu[i]] && bo[ev[i]]) ? dis[eu[i]] - dis[ev[i]] : 1) << '\n';
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/R-Q-R-Q/p/12689996.html