版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ccsu_cat/article/details/82118252
题意:你可以一行全涂X,也可以一列全涂O,可以覆盖,输出字典序最小的一个涂色方案,使得从一个空白的n*n的矩形可以变成给定的矩形。
思路:正过来想不好想,可以反过来想,如果一行全为X,那么这一行肯定是最后涂的,那么就把这一行X全部擦掉,如果这一步造成某一列只剩下O,那么剩下的涂色方案中,这一列是最后涂的颜色,这么一想,有点像拓扑排序,设R数组度数为每一行O的个数,C数组度数为每一列X的个数,那么接下来要做的就是拓扑排序,将所有度数为0的点进队,如果出队点R[ i ],接下来会造成所有的C点度数减一,如果有一个点C[ j ]的度数因此变为0,将C[ j ]入队即可。每次出队点C[ i ],也会造成多有的R点度数减一,同理。
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn=505;
char s[maxn][maxn];
int n,r[maxn],c[maxn];
struct node
{
char c;
int num;
bool operator<(const node& t)const//优先队列输出字典序最大,然后用栈变成字典序最小
{
if(c==t.c)
return num<t.num;
return c<t.c;
}
}no;
void toposort()
{
stack<node>sta;
priority_queue<node>q;
for(int i=1;i<=n;i++)
if(!r[i])
q.push((node){'R',i});
else if(!c[i])
q.push((node){'C',i});
int cnt1=0,cnt2=0;//分别记录涂了的行数和列数
while(!q.empty())
{
no=q.top();q.pop();
if(cnt1<n&&cnt2<n)sta.push(no);//如果行数涂满了或者列数涂满了,就不用涂色了
if(no.c=='R')cnt1++;
else cnt2++;
if(no.c=='R')
for(int i=1;i<=n;i++)
{
c[i]--;
if(!c[i])
q.push((node){'C',i});
}
else
for(int i=1;i<=n;i++)
{
r[i]--;
if(!r[i])
q.push((node){'R',i});
}
}
int flag=1;
for(int i=1;i<=n;i++)if(r[i]>0||c[i]>0)flag=0;//有些点的度数不为0,无解
if(!flag)
puts("No solution");
else
while(!sta.empty())
{
no=sta.top();
sta.pop();
printf("%c%d",no.c,no.num);
if(!sta.empty())
printf(" ");
else
puts("");
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)
{
int cnt1=0,cnt2=0;
for(int j=1;j<=n;j++)
{
if(s[i][j]=='O')
cnt1++;
if(s[j][i]=='X')
cnt2++;
}
r[i]=cnt1,c[i]=cnt2;
}
toposort();
}
}