Matrix 矩阵 NKOJ P6277、51Nod 2853、HYSBZ 4500、黑暗爆炸 4500 题解 令人耳目一新的差分约束做法

题目描述

这里以校内 OJ 上的题面为准,对 Markdown 不合规之处进行了调整。

何老板给你一个 \(n \times m\) 的数字矩阵,开始时矩阵中每个格子里的数字都为 \(0\),何老板可以对矩阵进行如下两种操作:

  1. 任选一行,让该行所有格子里的数字都增加 \(1\) 或减少 \(1\)
  2. 任选一列,让该列所有格子里的数字都增加 \(1\) 或减少 \(1\)

何老板给你有 \(K\) 个限制条件,每个限制条件形为 \(x,y,z\) 三个整数,表示要让格子 \((x,y)\) 里的数字为 \(z\)

何老板想知道经过若干次上述操作后,能否使得该矩阵满足所有 \(K\) 个限制条件?如果能输出 Yes,否则输出 No

多测,\(T <= 5,1 \le n,m,K \le 1000\)

\(|z| \le 10^4\)

思路 差分约束

似乎跟网上大部分题解的做法不一样。至少网上的大部分题解太简短,一句话了事,看不懂。

考虑这样一件事:对于处在同一行的两个格子 \((x_1,y),(x_2,y)\)\(x_1,x_2\) 两列的操作次数之差(规定加法为加一次操作次数,减法为减一次操作次数,所以如果操作次数为负,表示执行绝对值次减法操作)显然为 \(z_1-z_2\)

因为第 \(y\) 行的操作次数是固定的,所以让 \(z_1 \not = z_2\) 的唯一办法就是让这两列的操作次数不同。(这个应该不难想吧?

所以令 \(d_{x_i}\) 表示对第 \(x_i\) 列进行 \(d_{x_i}\) 次操作,那么通过上面的分析,我们就得出了一组等量关系:\(d_{x_1}-d_{x_2} = z_1-z_2\)。把这个式子转化为差分约束的不等式:

\[d_{x_1}-d_{x_2} \le z_1-z_2 \]

\[d_{x_1}-d_{x_2} \ge z_1-z_2 \]

第一式可以直接建边,从 \(x_1\)\(x_2\) 连一条权值为 \(z_1-z_2\) 的边。第二式化简得:

\[\begin{aligned} d_{x_1}-d_{x_2} & \ge z_1-z_2 \\ d_{x_1}-(z_1-z_2) & \ge d_{x_2} \\ d_{x_2} & \le d_{x_1}-(z_1-z_2) \\ \end{aligned}\]

这表示从 \(x_2\)\(x_1\) 连一条权值为 \(-(z_1-z_2)\) 的边。这样等量关系就表达完成。

同一列的格子同理。\(O(k^2)\) 枚举每一对关系,暴力建边。

注意图不联通,但是我们一对关系建的相当于是双向边,所以每次从没有跑过差分约束的点开始跑一遍 SPFA 即可。

无解即出现负环。

时间复杂度:\(O(k^2+(n+m)k^2)\),不过由于 SPFA 本身在一般图上跑的不错,所以 \(O((n+m)k^2)\) 的部分跑不满。

AC 代码

#include<bits/stdc++.h>
using namespace std;
int t;
int n,m,q;
vector<pair<int,int>> hang[1050],lie[1050];
int etot = 0,Last[2050],Next[2000050],End[2000050],Len[2000050];
void addedge(int u,int v,int c){
	etot++;
	Next[etot] = Last[u];
	Last[u] = etot;
	End[etot] = v;
	Len[etot] = c;
	return;
}
int dis[2050],tot[2050];
bitset<2050> inque;
queue<int> que;
bool spfa(int Start){
	inque.reset();
	while(que.size()){
		que.pop();
	}
	dis[Start] = 0;
	que.push(Start);
	inque[Start] = 1;
	int now;
	while(que.size()){
		now = que.front();
		que.pop();
		inque[now] = 0;
		tot[now]++;
		if(tot[now] > n+m){
			return 1;
		}
		for(int i = Last[now]; i; i = Next[i]){
			if(dis[End[i]] > dis[now]+Len[i]){
				dis[End[i]] = dis[now]+Len[i];
				if(inque[End[i]]){
					continue;
				}
				inque[End[i]] = 1;
				que.push(End[i]);
			}
		}
	}
	return 0;
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d",&n,&m,&q);
		for(int i = 0; i <= n+10; i++){
			hang[i].clear();
		}
		for(int i = 0; i <= m+10; i++){
			lie[i].clear();
		}
		memset(Last,0,sizeof(int)*(n+m+10));
		memset(Next,0,sizeof(int)*(etot+10));
		memset(End,0,sizeof(int)*(etot+10));
		memset(Len,0,sizeof(int)*(etot+10));
		etot = 0;
		while(q--){
			static int tpa,tpb,tpc;
			scanf("%d%d%d",&tpa,&tpb,&tpc);
			hang[tpa].push_back({tpb,tpc});
			lie[tpb].push_back({tpa,tpc});
		}
		for(int i = 1; i <= n; i++){
			static int tp;
			sort(hang[i].begin(),hang[i].end());
			for(int j = 0; j < (int)hang[i].size(); j++){
				for(int k = j+1; k < (int)hang[i].size(); k++){
					tp = hang[i][j].second-hang[i][k].second;
					addedge(hang[i][j].first+n,hang[i][k].first+n,-tp);
					addedge(hang[i][k].first+n,hang[i][j].first+n,tp);
				}
			}
		}
		for(int i = 1; i <= m; i++){
			static int tp;
			sort(lie[i].begin(),lie[i].end());
			for(int j = 0; j < (int)lie[i].size(); j++){
				for(int k = j+1; k < (int)lie[i].size(); k++){
					tp = lie[i][j].second-lie[i][k].second;
					addedge(lie[i][j].first,lie[i][k].first,-tp);
					addedge(lie[i][k].first,lie[i][j].first,tp);
				}
			}
		}
		memset(dis,0x3f,sizeof(int)*(n+m+10));
		memset(tot,0,sizeof(int)*(n+m+10));
		for(int i = 1; i <= n+m; i++){
			if(dis[i] == dis[0]){
				if(spfa(i)){
					puts("No");
					goto gt;
				}
			}
		}
		puts("Yes");
		gt:;
	}
	return 0;
}
posted @ 2025-02-10 23:31  ny_Dacong  阅读(45)  评论(0)    收藏  举报