当前位置: 首页 > news >正文

【差分约束】P5590 赛车游戏|省选-

本文涉及知识点

【数学 线性代数】差分约束

P5590 赛车游戏

题目描述

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

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

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

输入格式

第一行两个整数 n , m n,m n,m

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

输出格式

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

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

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

输入输出样例 #1

输入 #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 30 30 分): n ≤ 10 n \leq 10 n10 m ≤ 20 m \leq 20 m20
Subtask #2( 30 30 30 分): n ≤ 100 n \leq 100 n100 m ≤ 200 m \leq 200 m200
Subtask #3( 40 40 40 分): n ≤ 1000 n \leq 1000 n1000 m ≤ 2000 m \leq 2000 m2000

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

P5590 赛车游戏 差分约束

一,以1为起点BFS,如果无法到达N点,返回-1。
二,所有边反转,从N开始BFS。两轮BFS都到达的点才处理,有一轮没达到的点及相连的边删除。
三,假定各边的权重为1,求各节点到1的最长路leves[i]。如果有环(正环),返回-1。
四,按leves[i] 从大到小枚举各节点, 枚举i的后续节点j。m1 = min(leves[j]),m2=max(leves[j]),有leves[i] < leves[j],故leves[i] < m1恒成立。确保leves[i] < m1的前提上, 增加leves[i] 及其前置节点,使得leves[j]-leves[i] <=9.
第四步完成后,leves[j] - leves[i] 就是i到j 的权。如果leves[i] 有多个合法值,都要枚举。
综合3到5就是差分约束。leves[i] < leves[j] ,即levevs[i] -leves[j] <=-1,leves[j]-leves[i] <=9。由于levevs[i] -leves[j]<=-1,地图有环,差分系统的有向图也一定有环。
由于边权最大9,边数1000。故1e4就是极大值。增加0到各点权位1e4的边。以0为起点,求最小路径。

总结

删除非起点终点路径上的点和边后。起点到任意点的任意路径路程都相等,否则和起点到终点任意路径相等矛盾。vis[i]记录起点到i的最短路,也就是起点i到任意路径的路程。
u有边指向v,则:uv的边权是dis[v]-dis[u],可以是1到9的任意值。
本题是差分约束的充分条件,故差分约束,无解,则本题无解。
下面用数学归纳法来证明差分的系统的解一定符合所有路径都是最短路。
经过0条边的路径,只有一条,一定符合本题。
如果经过i条边的路径都符合,则经过i+1条边的路径也符合。令第i+1条边是u → \rightarrow v,则此路径的路程是:dis[u]+(dis[v]-disv[u])= div[v]就是最短路。
注意:忽略的边任意权值。

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t);
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	scanf("%d", &n);
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<int N = 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
		AuotToFile();
	}
	void writestr(const char* sz) {
		strcpy(m_p, sz);
		m_p += strlen(sz);
		AuotToFile();
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
		AuotToFile();
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
		m_p = puffer;
	}
	~COutBuff() {
		ToFile();
	}
private:
	inline void AuotToFile() {
		if (m_p - puffer > N - 100) {
			ToFile();
		}
	}
	char  puffer[N], * m_p;
};

template<int N = 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {}
	inline CInBuff<N>& operator>>(char& ch) {
		FileToBuf();
		ch = *S++;
		return *this;
	}
	inline CInBuff<N>& operator>>(int& val) {
		FileToBuf();
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行		
		return *this;
	}
	inline CInBuff& operator>>(long long& val) {
		FileToBuf();
		long long x(0); int f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行
		return *this;
	}
	template<class T1, class T2>
	inline CInBuff& operator>>(pair<T1, T2>& val) {
		*this >> val.first >> val.second;
		return *this;
	}
	template<class T1, class T2, class T3>
	inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val);
		return *this;
	}
	template<class T1, class T2, class T3, class T4>
	inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);
		return *this;
	}
	template<class T = int>
	inline CInBuff& operator>>(vector<T>& val) {
		int n;
		*this >> n;
		val.resize(n);
		for (int i = 0; i < n; i++) {
			*this >> val[i];
		}
		return *this;
	}
	template<class T = int>
	vector<T> Read(int n) {
		vector<T> ret(n);
		for (int i = 0; i < n; i++) {
			*this >> ret[i];
		}
		return ret;
	}
	template<class T = int>
	vector<T> Read() {
		vector<T> ret;
		*this >> ret;
		return ret;
	}
private:
	inline void FileToBuf() {
		const int canRead = m_iWritePos - (S - buffer);
		if (canRead >= 100) { return; }
		if (m_bFinish) { return; }
		for (int i = 0; i < canRead; i++)
		{
			buffer[i] = S[i];//memcpy出错			
		}
		m_iWritePos = canRead;
		buffer[m_iWritePos] = 0;
		S = buffer;
		int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);
		if (readCnt <= 0) { m_bFinish = true; return; }
		m_iWritePos += readCnt;
		buffer[m_iWritePos] = 0;
		S = buffer;
	}
	int m_iWritePos = 0; bool m_bFinish = false;
	char buffer[N + 10], * S = buffer;
};

template<class T = int, T iDef = INT_MAX / 2>
class CDisNegativeRing //贝尔曼-福特算法
{
public:
	bool Dis(int N, vector<tuple<int, int, int>> edgeFromToW, int start) {
		vector<T> pre(N, iDef);
		pre[start] = 0;
		for (int t = 0; t < N; t++) {
			auto cur = pre;
			for (const auto& [u, v, w] : edgeFromToW) {
				cur[v] = min(cur[v], pre[u] + w);
			}
			if (t + 1 == N) {
				for (int i = 0; i < N; i++) {
					if (pre[i] != cur[i]) { return false; }
				}
			}
			pre.swap(cur);
		}
		m_vDis = pre;
		return true;
	}
	vector<T> m_vDis;
};

class CNeiBo
{
public:
	static vector<vector<int>> Two(int n, const vector<pair<int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& [i1, i2] : edges)
		{
			vNeiBo[i1 - iBase].emplace_back(i2 - iBase);
			if (!bDirect)
			{
				vNeiBo[i2 - iBase].emplace_back(i1 - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Two(int n, const vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat)
	{
		vector<vector<int>> neiBo(neiBoMat.size());
		for (int i = 0; i < neiBoMat.size(); i++)
		{
			for (int j = i + 1; j < neiBoMat.size(); j++)
			{
				if (neiBoMat[i][j])
				{
					neiBo[i].emplace_back(j);
					neiBo[j].emplace_back(i);
				}
			}
		}
		return neiBo;
	}
};
class CBFSDis
{
public:
	CBFSDis(vector<vector<int>>& vNeiB, vector<int> start)
	{
		m_vDis.assign(vNeiB.size(), m_iNotMayDis);
		queue<int> que;
		for (const auto& n : start)
		{
			m_vDis[n] = 0;
			que.emplace(n);
		}
		while (que.size())
		{
			const int cur = que.front();
			que.pop();
			for (const auto next : vNeiB[cur])
			{
				if (m_iNotMayDis != m_vDis[next])
				{
					continue;
				}
				m_vDis[next] = m_vDis[cur] + 1;
				que.emplace(next);
			}
		}
	}
public:
	const int m_iNotMayDis = 1e9;
	vector<int> m_vDis;
};
class Solution {
public:
	vector<int> Ans(int N, const vector<pair<int, int>>& edge) {
		auto neiBo = CNeiBo::Two(N, edge, true, 1);
		vector<vector<int>> neiBoBack(N);
		for (int i = 0; i < N; i++) {
			for (const auto& j : neiBo[i]) {
				neiBoBack[j].emplace_back(i);
			}
		}
		CBFSDis dis1(neiBo, { 0 });
		if (dis1.m_iNotMayDis == dis1.m_vDis.back()) { return {}; }
		CBFSDis dis2(neiBoBack, { N - 1 });
		vector<tuple<int, int, int>> edge2;
		auto Not = [&](int i, int j) {
			if ((dis1.m_vDis[i] > N) || (dis1.m_vDis[j] > N)) { return true; }
			if ((dis2.m_vDis[i] > N) || (dis2.m_vDis[j] > N)) { return true; }
			return false;
		};
		for (auto [i, j] : edge) {
			i--, j--;
			if (Not(i, j)) { continue; }
			//eves[i] < leves[j] ,即levevs[i] -leves[j] <=-1,leves[j]-leves[i] <=9。由于levevs[i] -leves[j]<=-1,
			edge2.emplace_back(j, i, -1);
			edge2.emplace_back(i, j, 9);
		}
		CDisNegativeRing dis;
		if (!dis.Dis(N, edge2, 0)) { return {}; }
		vector<int> ans;
		for (auto [i, j] : edge) {
			i--, j--;
			if (Not(i, j)) { ans.emplace_back(9); continue; }
			ans.emplace_back(dis.m_vDis[j] - dis.m_vDis[i]);
		}
		return ans;
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	ios::sync_with_stdio(0);
	int n, m ;
	cin >> n >> m ;	
	auto ope = Read<pair< int,int>>(m);
#ifdef _DEBUG		
	printf("n=%d", n);
	Out(ope, "edge=");
	//Out(ope, ",ope=");
	//Out(edge2, ",edge2=");
	/*Out(que, "que=");*/
#endif // DEBUG	
	auto res = Solution().Ans(n,ope);
	if (res.empty()) {
		cout << -1;
	}
	else {
		cout << n << " " << m << "\n";
		for (int i = 0; i < m; i++) {
			cout << ope[i].first << " " << ope[i].second << " " << res[i] << "\n";
		}
	}
	return 0;
}

单元测试

int n;
		vector<pair<int, int>> edge;
		TEST_METHOD(TestMethod1)
		{
			n = 3, edge = { {1,2} };
			auto res = Solution().Ans(n, edge);
			AssertV({}, res);
		}
		TEST_METHOD(TestMethod2)
		{
			n = 10,edge = { {1,2},{2,3},{3,4},{4,5},{5,6},{6,7},{7,8},{8,9},{9,10},{1,10} };
			auto res = Solution().Ans(n, edge);
			vector<int >act(edge.size(), 1);
			act.back() = 9;
			AssertV(act, res);
		}
		TEST_METHOD(TestMethod3)
		{
			n = 3, edge = { {1,3},{1,2} };
			auto res = Solution().Ans(n, edge);
			AssertV({9,9}, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

相关文章:

  • 微软OneNote无法同步解决方案
  • 模运算专题练习 ——基于罗勇军老师的《蓝桥杯算法入门C/C++》
  • 2025-03-17 Unity 网络基础1——网络基本概念
  • 学习单片机需要多长时间才能进行简单的项目开发?
  • 鸿蒙应用开发--数据埋点的名称由来,发展脉络,典型场景,现代演进的无埋点和智能化埋点//学习时长数据埋点的实现--待更新
  • 如何在 GoLand 中设置默认项目文件夹
  • 树莓派学习:环境配置
  • 《基于深度学习的高分卫星图像配准模型研发与应用》开题报告
  • 基于Spring Boot的红色革命文物征集管理系统的设计与实现(LW+源码+讲解)
  • Java高频面试之集合-13
  • 【ACM 独立出版 | EI 快检索】2025年数据挖掘与项目管理国际研讨会 (DMPM 2025)
  • 如何使用MySQL快速定位慢SQL问题?企业级开发中常见业务场景中实际发生的例子,涉及分页查询问题。(二)
  • LLMs之CoTM:《Detecting misbehavior in frontier reasoning models》翻译与解读
  • Linux驱动学习笔记(零)
  • [设计模式与源码]1_Spring三级缓存中的单例模式
  • 设计模式(行为型)-状态模式
  • Leetcode 刷题笔记1 单调栈part01
  • UART转AHB模块ModelSim仿真
  • C语言每日一练——day_10
  • 冒泡排序:古老算法中的智慧启示
  • 辽宁省全力开展辽阳一饭店火灾事故救援处置工作
  • 直播电商行业代表呼吁:携手并肩伸出援手助力外贸企业攻坚克难
  • 上海74岁老人宜春旅游时救起落水儿童,“小孩在挣扎容不得多想”
  • 对谈|李钧鹏、周忆粟:安德鲁·阿伯特过程社会学的魅力
  • 深圳宝安区一宗涉宅用地中止出让,起始总价86.27亿元
  • 黄永年:说狄仁杰的奏毁淫祠