首页 > 其他分享 >高手训练 负环 题解

高手训练 负环 题解

时间:2024-04-04 20:55:34浏览次数:20  
标签:高手 int 题解 负环 点数 rightarrow 单调 dis

题目链接

方向:枚举点的个数,找出其中边权和为负数的最小值。

直接枚举显然会超时,不妨考虑使用倍增凑出点的个数(注意:点数不完全有单调性,但是后面会提到如何转化处理)。

先预处理出 \(dis_{t,i,j}\) 表示经过 \(t\) 条边,从 \(i\rightarrow j\) 的最短路长度。那么类似 \(Floyed\) 显然有如下式子:

\[dis_{t,i,j}=\min_{1\le k\le n}\{dis_{t-1,i,k}+dis_{t-1,k,j}\} \]

可以在 \(O(n^3\log n)\) 中算出。

接下来计算 \(f_{i,j}\) 表示从 \(i\rightarrow j\) 的最短路,点数则在倍增中处理,如果存在 \(f_{i,i}<0\),说明有包含 \(i\) 的负环。

接着考虑倍增基本套路:从大到小枚举 \(t\),尝试将当前点数的 \(dis\) 值加入 \(f\) 中,并考虑有无负环,如果有,则不做变化;如果没有,就将当前点数的 \(2\) 次幂加入答案,最后将答案 \(+1\) 即可。(同 \(LCA\) 套路)。

使用如下式子即可将 \(dis\) 加入 \(f\):

\[f'_{i,j}=\min\{f_{i,k}+dis_{t,k,j},dis_{t,i,k}+f_{k,j}\} \]

注意,\(f'\) 和 \(f\) 是两个东西,如果两边都写 \(f\),那么就不止加 \(2^t\) 条边。

如果没有负环,再将 \(f'\) 的值保存进 \(f\) 里。

但是这样还有瑕疵,因为点数不具备单调性,\(3\) 个点可以,可能 \(4\) 个点就不行。考虑在每个点上加一个自环,即:\(i\rightarrow i\),\(f_{i,i}=dis_{0,i,i}=0\),那么 \(dis_{t,i,j}\) 实际上就转化为至多经过 \(2^t\) 条边,从 \(i\rightarrow j\) 的最短路径。(因为其中可能走过一些自环边,在原题中相当于没走),从恰好转化为至多后,必然符合单调性,可以倍增。

总结:

  • 单调性转化:如果恰好的数量不符合单调性,可以考虑至多/至少的数量,这样一定符合单调性;
  • 恰好与至多至少转化:如果题目含有两个需考虑的值,而做法是考虑其中一种,那么可以往其中加入对一个值有影响但是对另外一个值无影响的变量;
  • 倍增:题目具备单调性,求最值。

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=310;
int n,m;
int dis[10][N][N],f[N][N],g[N][N];

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);

	cin>>n>>m;
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=n;i++) {
		f[i][i]=0;
	}
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=m;i++) {
		int u,v,w; cin>>u>>v>>w;
		dis[0][u][v]=w;
	}
	for(int i=1;i<=n;i++) dis[0][i][i]=0;
	for(int t=1;t<=9;t++) {
		for(int k=1;k<=n;k++) {
			for(int i=1;i<=n;i++) {
				for(int j=1;j<=n;j++) {
					dis[t][i][j]=min(dis[t][i][j],dis[t-1][i][k]+dis[t-1][k][j]);
				}
			}
		}
	}
	int ans=0;
	for(int t=9;t>=0;t--) {
		memcpy(g,f,sizeof(f));
		for(int k=1;k<=n;k++) {
			for(int i=1;i<=n;i++) {
				for(int j=1;j<=n;j++) {
					g[i][j]=min(g[i][j],min(f[i][k]+dis[t][k][j],dis[t][i][k]+f[k][j]));
				}
			}
		}
		int pd=0;
		for(int i=1;i<=n;i++) {
			if(g[i][i]<0) {pd=1;break;}
		}
		if(!pd) {
			ans|=(1<<t);
			memcpy(f,g,sizeof(g));
		}
	}
	if(ans>=n) cout<<0;
	else cout<<ans+1;
    return 0;
}

标签:高手,int,题解,负环,点数,rightarrow,单调,dis
From: https://www.cnblogs.com/zhangyuzhe/p/18114588

相关文章

  • 少儿编程 2024年3月电子学会图形化编程等级考试Scratch一级真题解析(选择题)
    2024年3月scratch编程等级考试一级真题选择题(共25题,每题2分,共50分)1、单击下列哪个按钮,能够让舞台变为“全屏模式”A、B、C、D、答案:C考点分析:考查scratch平台的使用,四个选项分别是:开始程序,停止程序,全屏模式,恢复正常模式,答案C2、下列哪个选项可以将当前背景换成第二......
  • win server系统物理机转成虚拟机出现 计算机丢失api-ms-win-crt-stdio-|1-1-0.dll问题
     物理机转移虚拟机的方案有很多种,这里讲下官方的这个转移工具转移,很简单下载下来一步步跟着点就好了。但是server系统的话可能会出现如图这样子的报错,缺少dll文件,这是因为server系统本身缺少这个文件组,解决方式有两种:1.去下载dll表文件,放置对应的文件夹下面,重新迁移2.利用......
  • C语言 | Leetcode C语言题解之第8题字符串转换整数atoi
    题目:题解:intmyAtoi(char*s){inti=0;intout=0;intpol=1;intlen=strlen(s);if(len==0)return0;while(s[i]=='')i++;//删除空格if(s[i]=='-'){//判断正负pol=-1;i++;}else......
  • Java | Leetcode Java题解之第10题正则表达式匹配
    题目:题解:classSolution{publicbooleanisMatch(Strings,Stringp){intm=s.length();intn=p.length();boolean[][]f=newboolean[m+1][n+1];f[0][0]=true;for(inti=0;i<=m;++i){......
  • Java | Leetcode Java题解之第9题回文数
    题目:题解:classSolution{publicbooleanisPalindrome(intx){//特殊情况://如上所述,当x<0时,x不是回文数。//同样地,如果数字的最后一位是0,为了使该数字为回文,//则其第一位数字也应该是0//只有0满足这一属......
  • Java | Leetcode Java题解之第8题字符串转换整数atoi
    题目:题解:classSolution{publicintmyAtoi(Stringstr){Automatonautomaton=newAutomaton();intlength=str.length();for(inti=0;i<length;++i){automaton.get(str.charAt(i));}retur......
  • Java | Leetcode Java题解之第7题整数反转
    题目:题解:classSolution{publicintreverse(intx){intrev=0;while(x!=0){if(rev<Integer.MIN_VALUE/10||rev>Integer.MAX_VALUE/10){return0;}intdigit=x......
  • 洛谷p1002题解
    [NOIP2002普及组]过河卒题目描述棋盘上AAA点有一个过河卒,需要走到目标BB......
  • CF371E Subway Innovation 题解
    题目链接:CF或者洛谷对于绝对值的几何意义来说,这题是在直线上的两点间的距离,为了总的距离和最下,首先最好让它们两两之间最好都紧挨着。由于询问的是\((i,j)\)不重不漏的对有关,即\((i<j)\+\(i>j)\+\(i=j)=all(i,j)\),又因为,\((i,j)\)的贡献和\((j,i)\)相同且重复,所以我......
  • [ABC223F] Parenthesis Checking 题解
    [ABC223F]ParenthesisChecking题解思路解析在开始之前,首先我们需要知道合法括号序列的判断方法。我们可以给每个括号打上权值,设左括号权值为\(1\),右括号权值为\(-1\),这样一个\(\texttt{(()())}\)括号串用数字存下就是\(1,1,-1,1,-1,-1\),这时我们再给序列计算一下前缀和......