题解:P11476 [COCI 2024/2025 #3] 涂矩阵 / Bojanje
题意
我们有一个
思路
我们某一行或者某一列一定不会被染色多次,因此操作次数小于等于
考虑我们这样一直将可以染色的行或列全部删除直到没有可删除的行或列了该怎么办。我们发现,如果此时还有没有被染色正确的点的话就一定无解了。为什么呢,我们发现,此时无论如何染某一行或列,都一定会有不满足最终矩阵的位置。因此我们只需要一直操作到不能继续操作,再按照找到的操作还原矩阵判断一下与最终矩阵相不相同即可。
代码
#include<bits/stdc++.h>
#define N 2000 + 39
using namespace std;
int a[N][N], h[3][N], l[3][N], n, c[N][N];
bool vis[2][N];
struct Node
{
int op, x, color;
};
stack<Node>ans, check;
queue<Node>q;
int main()
{
ios :: sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
{
cin >> a[i][j];
h[a[i][j]][i] ++;
l[a[i][j]][j] ++;//我们记录每一行每一列0,1,2的个数,这样就能快速找到全1或2的行或列
}
}
for(int i = 1; i <= n; i ++)
{
if(h[0][i])//有0一定不能染色
{
vis[0][i] = 1;//判断一行或一列能否被染色,有0的显然不行
}
else
{
if(!h[1][i])
{
vis[0][i] = 1;
q.push({1, i, 2});
}
else if(!h[2][i])
{
vis[0][i] = 1;
q.push({1, i, 1});
}
}
if(l[0][i])
{
vis[1][i] = 1;
}
else
{
if(!l[1][i])
{
vis[1][i] = 1;
q.push({2, i, 2});
}
else if(!l[2][i])
{
vis[1][i] = 1;
q.push({2, i, 1});
}
}
}
while(q.size())//一直寻找可以被操作的位置
{
check.push(q.front());
ans.push(q.front());
int op = q.front().op, x = q.front().x, color = q.front().color;
q.pop();
if(op == 1)
{
for(int i = 1; i <= n; i ++)
{
if(vis[1][i])
{
continue;
}
l[color][i] --;//修改行的时候更新每一列
if(l[color][i] == 0)
{
vis[1][i] = 1;
q.push({2, i, color == 1 ? 2 : 1});
}
}
}
else
{
for(int i = 1; i <= n; i ++)
{
if(vis[0][i])
{
continue;
}
h[color][i] --;//同理
if(h[color][i] == 0)
{
vis[0][i] = 1;
q.push({1, i, color == 1 ? 2 : 1});
}
}
}
}
while(check.size())//还原一下我们的操作的最终矩阵
{
int op = check.top().op, x = check.top().x, color = check.top().color;
check.pop();
for(int i = 1; i <= n; i ++)
{
if(op == 1)
{
c[x][i] = color;
}
else
{
c[i][x] = color;
}
}
}
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
{
if(c[i][j] != a[i][j])
{
cout << -1;//不合法输出-1
return 0;
}
}
}
cout << ans.size() << '\n';
while(ans.size())
{
cout << ans.top().op << ' ' << ans.top().x << ' ' << ans.top().color << '\n';
ans.pop();
}
return 0;
}
AC 记录。