最近敲码的时候发现,自己以前学的一些算法都忘记了,甚至连名字都记不住。所以我又开始写文章来进行个人记录。
题意
给定n*m的二维矩阵。要求
- 每个格子只包含0,1,2。
- 包含任意2*2的小矩阵,都需要包含3种类型的元素。
- 任意相邻元素(相同行或相同列的相邻元素),不能是同一个元素。
初始之外,给定k对对角元素(正对角或反对角),要求这k对元素,每对之间两两相等。
问是否存在这样构造的二维矩阵,如果存在,输出yes,否则输出no
这是我第一次接触二分图的算法,花了好长的时间去看官方题解的代码,仔细思考,最后顺着它的代码手跑了一遍,才对二分图稍微有一点儿了解。所谓二分图,大多数类似的是染色,将条件限制的某几个方块染色,最后来判断改图是否能够形成。尽管如此,想把这道题给转换成二分图的形式还是很困难的。
首先,看x与y,我们发现无非是存在两种情况,(x,y)与(x+1,y+1)相等以及(x+1,y)与(x,y+1)相等,我们完全可以把这看成是两种情况,分别用0与1两个值来表示,从而构建双向图,以0与1为两点之间的边权值。这样,我们就成功把这个问题转化为了一个染色的问题。但染色也成了一个问题,我们发现,对于一个2X2的小方格而言,左上角的方块与右侧和下侧的差值是相等的,这说明,我们完全可以通过差值来染色,而不用去纠结数字,差值无非只用两个1,2,我们可以用对应的0,1来进行代替,实现对二分图的初步转化。然后用图通用的遍历方式dfs经行遍历,时间复杂度戳戳有余。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const ll N=998244353;
ll n,m,k;
bool f=0;
int main(){
ios::sync_with_stdio(false);
cin.tie();
cout.tie();
int t;
cin>>t;
while(t--){
cin>>n>>m>>k;
f=0;
vector<vector<PII>>ve(n+m+5);
vector<int>c(n+m+5,-1);
for(int i=1;i<=k;i++){
int x,y,xx,yy;
cin>>x>>y>>xx>>yy;
ve[min(x,xx)].push_back({min(y+n,yy+n),x+y!=xx+yy});
ve[min(y+n,yy+n)].push_back({min(x,xx),x+y!=xx+yy});
}
function<void(int,int)>dfs=[&](int x,int fa)->void{
if(c[x]!=-1){
if(c[x]!=fa){
f=1;
}
return;
}
c[x]=fa;
for(auto [i,j]:ve[x])dfs(i,j^fa);
};
for(int i=1;i<=n+m;i++){
if(c[i]==-1)dfs(i,0);
if(f)break;
}
if(f)printf("NO\n");
else printf("YES\n");
}
}