编程语言
首页 > 编程语言> > 图论杂算法选讲

图论杂算法选讲

作者:互联网

差分约束

例题:P5960 【模板】差分约束算法

给出一组包含 \(m\) 个不等式,有 \(n\) 个未知数的形如:

\[\begin{cases} x_{c_1}-x_{c'_1}\leq y_1 \\x_{c_2}-x_{c'_2} \leq y_2 \\ \cdots\\ x_{c_m} - x_{c'_m}\leq y_m\end{cases} \]

的不等式组,求任意一组满足这个不等式组的解。

对于 \(100\%\) 的数据,\(1\leq n,m \leq 5\times 10^3\),\(-10^4\leq y\leq 10^4\),\(1\leq c,c'\leq n\),\(c \neq c'\)。

对于这种解不等式问题,我们可以使用差分约束算算法。

首先,先建立一个超级源点 \(0\),然后对于 \(a - b\leq c\),连边 \((b,a,c)\),最后跑最短路即可,无解就是存在负环。

这一步可以用SPFA,Dijkstra不行。

时间复杂度 \(O(nm)\)。

伪代码如下:

Input n,m;
For i in Range(1,n){
	add_edge(0,i,0);
}
For i in Range(1,m){
	Input from,to,weight;
	add_edge(from,to,weight);
}
res=Spfa(start=0);
If(res is not){
	Print "NO";
}
Else{
	For i in Range(1,n){
		Print Dis[i];
		Putchar ' ';
	}
}
end

P1993 小 K 的农场

小 K 在 \(\texttt{Minecraft}\) 里面建立很多很多的农场,总共 \(n\) 个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共 \(m\) 个),以下列三种形式描述:

但是,由于小 K 的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。

对于 \(100\%\) 的数据,保证 \(1 \le n,m,a,b,c \le 5 \times 10^3\)。

对于形式 \(1\),即 \(b-a\leq -c\),连边 \((a,b,-c)\)。

对于形式 \(2\),即 \(a-b\leq c\),连边 \((b,a,c)\)。

对于形式 \(3\),即 \(a-b \leq 0\) 和 \(b-a\leq 0\),连边 \((a,b,0)\) 与 \((b,a,0)\)。

然后跑差分约束即可。

Johnson 全源最短路 | P5905 【模板】Johnson 全源最短路

给定一个包含 \(n\) 个结点和 \(m\) 条带权边的有向图,求所有点对间的最短路径长度,一条路径的长度定义为这条路径上所有边的权值和。

注意:

  1. 边权可能为负,且图中可能存在重边和自环;
  2. 部分数据卡 \(n\) 轮 SPFA 算法。

若图中存在负环,输出仅一行 \(-1\)。

若图中不存在负环:

输出 \(n\) 行:令 \(dis_{i,j}\) 为从 \(i\) 到 \(j\) 的最短路,在第 \(i\) 行输出 \(\sum\limits_{j=1}^n j\times dis_{i,j}\),注意这个结果可能超过 int 存储范围。

如果不存在从 \(i\) 到 \(j\) 的路径,则 \(dis_{i,j}=10^9\);如果 \(i=j\),则 \(dis_{i,j}=0\)。

对于 \(100\%\) 的数据,\(1\leq n\leq 3\times 10^3,\ \ 1\leq m\leq 6\times 10^3,\ \ 1\leq u,v\leq n,\ \ -3\times 10^5\leq w\leq 3\times 10^5\)。

注意给出的图不一定连通。

这道题卡 \(\texttt{Floyd, Dijkstra, SPFA}\) 等算法,但是不卡 \(\texttt{Johnson}\)。

\(\texttt{Johnson}\),就是将 \(\texttt{SPFA}\) 与 \(\texttt{Dijkstra}\) 结合,具体操作如下:

先建立超级源 \(0\) ,然后跑 \(\texttt{SPFA}\),令 \(h_i\) 为 \(0 \to i\) 的最短路径,将原来的边 \((u,v,w)\) 设为 \((u,v,w+h_u-h_j)\),然后跑 \(n\) 遍 \(\texttt{Dijkstra}\) 即可,因为 \(w+h_u-h_j\geq 0\),最后记得还原路径边权。

时间复杂度 \(O(nm\log m)\)(\(\texttt{Dijkstra}\) 开启了堆优化)

伪代码如下:

Input n,m
For i in Range(1,m){
	Input u,v,w
	AddEdge(u,v,w)
}
For i in Range(1,n){
	AddEdge(0,i,0)
}
If(not SPFA::SPFA(start=0)){
	Print -1
	Exit
}
For i in Range(1,n){
	For j in ConnectedEdges(i){
		g[j].weight += (SPFA::dis[i]-SPFA::dis[g[j].to])
	}
}
For i in Range(1,n){
    int ans=0
    heap_dijkstra(start=i)
    For j in Range(1,n){
        If(dis[j]==1e9){
            ans+=j*1e9
        }
        Else{
            ans+=j*(dis[j]+SPFA::dis[j]-SPFA::dis[i])
        }
    }
    Print ans
    Putchar '\n'
}

2-SAT问题

P4782 【模板】2-SAT 问题

有 \(n\) 个布尔变量 \(x_1\)\(\sim\)\(x_n\),另有 \(m\) 个需要满足的条件,每个条件的形式都是 「\(x_i\) 为 true / false 或 \(x_j\) 为 true / false」。比如 「\(x_1\) 为真或 \(x_3\) 为假」、「\(x_7\) 为假或 \(x_2\) 为假」。

2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。

如无解,输出 IMPOSSIBLE;否则输出 POSSIBLE

下一行 \(n\) 个整数 \(x_1\sim x_n\)(\(x_i\in\{0,1\}\)),表示构造出的解。

\(1\leq n, m\leq 10^6\) , 前 \(3\) 个点卡小错误,后面 \(5\) 个点卡效率,数据随机生成。

然后跑 \(\texttt{Tarjan}\) 强连通分量,如果 \(i\) 与 \(i^{'}\) 在同一个强连通分量,那么存在悖论,输出 \(\texttt{IMPOSSIBLE}\),否则输出 \(\texttt{POSSIBLE}\)。

最后输出答案,对于 \(\operatorname{SCC\_ID}(i) \lt \operatorname{SCC\_ID}(i^{'})\),输出 \(1\) 否则输出 \(0\)。

这次不放伪代码了,直接放C++程序片段吧。

int main(){
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int i_,a,j,b;
		cin>>i_>>a>>j>>b;
		if(a&&b){
			add(i_+n,j,114);
			add(j+n,i_,514);
		}
		if(!a&&b){
			add(i_,j,114);
			add(j+n,i_+n,514);
		}
		if(a&&!b){
			add(i_+n,j+n,114);
			add(j,i_,514);
		}
		if(!a&&!b){
			add(i_,j+n,114);
			add(j,i_+n,514);
		}
	}	
	for(int i=1;i<=(n<<1);i++){
		if(!dfsed[i]){
			tarjan(i);
		}
	}
	for(int i=1;i<=n;i++){
		if(c[i]==c[i+n]){
			cout<<"IMPOSSIBLE";
			return 0;
		}
	}
	cout<<"POSSIBLE"<<'\n';
	for(int i=1;i<=n;i++){
		cout<<((c[i]<c[i+n])?1:0)<<' ';
	}
	return 0;
} 

标签:10,图论,texttt,选讲,SPFA,leq,算法,add,dis
来源: https://www.cnblogs.com/zheyuanxie/p/graph-theory-algorithms.html