test 五彩斑斓 (拓扑序+建图)

本文介绍了一种使用图论中的拓扑排序解决特定矩阵粉刷问题的方法。通过对未直接染色的行进行枚举,确定每列的颜色,然后通过拓扑排序找出可行的染色顺序,确保不存在冲突,最终找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



题解:建图+拓扑序

这道题刚开始想的是乱搞和贪心。就是最后一次粉刷的行或列一定是颜色相同的,满足这个条件的一定是最后一次粉刷,然后将这次操作撤销,再寻找某行或某列全部是同种颜色(或被覆盖的),然后重复次过程。但是这样做存在反例:4 4 5
1 1 1 1
5 4 5 5
1 1 1 1
5 5 5 5  实际上先染一行5再染一列4,再把那1 3 4三行染上更优,但是上面的做法会把5分成三列染。

有一个重要的特点,就是每行每列至多被染一次,一定存在一行或者一列是没有被染过色的(就是都是染别的间接染上的,颜色不同)。

那么我就可以枚举没被染过色的行(或列,下面都以行为例)。然后其实就得知了每一列染的颜色(与这一行的对应位置颜色相同),然后我们扫描每行看是否有不和谐的格子,就是与当前列颜色不同的格子,需要通过粉刷行来满足,并且粉刷行一定再列之后。如果之前的格子要求此行的颜色与当前要求不同,那么产生冲突意味着无解,否则就从列向行连边。然后还要检查某些位置是否要求列的粉刷在行之前,然后连边。然后可以利用拓扑序得到一组合法的解,拓扑序中的点就是粉刷的方案。如果图中存在环则说明无解。

#include<iostream>  
#include<cstdio>  
#include<algorithm>  
#include<cmath>  
#include<cstring>  
#define N 53  
using namespace std;  
int n,m,c,coll[N],colh[N],ins[N*2],v[N*N*2],point[N*2],next[N*N*2],tot,q[N*N];  
int col[N][N],que[N*2],size,mark[N*2],ans,pt[N*2];  
int col1[N][N],que1[N*2],ans1,mark1[N*2],pt1[N*2];  
void add(int x,int y)  
{  
    tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; ins[y]++;  
    //cout<<x<<" "<<y<<endl;  
}  
bool solve()  
{  
    int head=0; int tail=0;   
    //for (int i=1;i<=n+m;i++) cout<<ins[i]<<" ";  
   // cout<<endl;  
    for (int i=1;i<=n;i++)  
     if (!ins[i]&&colh[i]&&colh[i]!=-1) q[++tail]=i;  
    for (int i=n+1;i<=n+m;i++)  
     if (!ins[i]&&coll[i-n]&&coll[i-n]!=-1) q[++tail]=i;  
    while (head<tail) {  
        int now=q[++head];  
        for (int i=point[now];i;i=next[i])  
         {  
            ins[v[i]]--;  
            if (!ins[v[i]]) q[++tail]=v[i];  
         }  
    }  
    size=tail;  
    for (int i=1;i<=n+m;i++)  
     if (ins[i]>0) return false;  
    return true;   
}  
bool check(int now)  
{  
    for (int i=1;i<=m;i++) coll[i]=col[now][i];  
    for (int i=1;i<=n;i++) colh[i]=-1; colh[now]=0;  
    tot=0;  
    memset(ins,0,sizeof(ins));  
    memset(point,0,sizeof(point));  
    //cout<<endl;  
    for (int i=1;i<=n;i++)  
    {  
     if (now==i) continue;
     for (int j=1;j<=m;j++)   
      if (col[i][j]!=coll[j]) {  
        if (col[i][j]==0&&coll[j]) return false;  
        if (colh[i]==-1) colh[i]=col[i][j];  
        else if (colh[i]!=col[i][j]) return false;  
      }  
     if (colh[i]==-1) colh[i]=0;
     if (!colh[i]) continue;  
     for (int j=1;j<=m;j++){  
        if (coll[j]==0) continue;  
        if (coll[j]!=col[i][j]) add(j+n,i);  
        else if (coll[j]==col[i][j]&&colh[i]!=coll[j]) add(i,j+n);  
     }  
    }  
    bool f=solve();    
    return f;  
}  
int main()  
{  
    freopen("iridescent.in","r",stdin);  
    freopen("iridescent.out","w",stdout);
    scanf("%d%d%d",&n,&m,&c);  
    for (int i=1;i<=n;i++)  
     for (int j=1;j<=m;j++) scanf("%d",&col[i][j]);  
    ans=n*m; ans1=n*m; bool pd=false;  
    for (int i=1;i<=n;i++)   
     if (check(i)&&size<ans) {  
        ans=size; que[0]=size; pd=true;  
        for (int j=1;j<=size;j++)   
          {  
            if (q[j]>n)  mark[j]=1,que[j]=q[j]-n,pt[j]=coll[que[j]];  
            else que[j]=q[j],mark[j]=0,pt[j]=colh[que[j]];  
          }  
     }  
    for (int i=1;i<=n;i++)  
     for (int j=1;j<=m;j++) col1[j][i]=col[i][j];  
    swap(n,m);  
    for (int i=1;i<=n;i++)  
     for (int j=1;j<=m;j++) col[i][j]=col1[i][j];  
    /*for (int i=1;i<=n;i++)  
     {  
        for (int j=1;j<=m;j++) cout<<col[i][j]<<" ";  
        cout<<endl;  
     }  */
    for (int i=1;i<=n;i++)   
      if (check(i)&&size<ans1) {  
        ans1=size; que1[0]=size; pd=true;  
        for (int j=1;j<=size;j++)   
          {  
            if (q[j]>n)  mark1[j]=1,que1[j]=q[j]-n,pt1[j]=coll[que1[j]];  
            else que1[j]=q[j],mark1[j]=0,pt1[j]=colh[que1[j]];  
          }  
     }  
    if (!pd) {  
        printf("-1\n");  
        return 0;  
    }  
    if (ans<ans1) {  
        printf("%d\n",ans);  
        for (int i=1;i<=ans;i++){  
            if (mark[i]) printf("C ");  
            else printf("R ");  
            printf("%d %d\n",que[i],pt[i]);  
        }  
        return 0;  
    }  
    printf("%d\n",ans1);  
    for (int i=1;i<=ans1;i++){  
        if (mark1[i]) printf("R ");  
        else printf("C ");  
        printf("%d %d\n",que1[i],pt1[i]);  
    }  
}  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值