zoj 3780 Paint the Grid Again 11届浙江省赛 (拓扑排序)

106 篇文章 0 订阅

题意:

给定一块正方形区域,大小为n,现在你能对每一行每一列进行一次操作,你可以 把一行涂成黑色,或者把一列涂成白色,但是每行每列都只能操作一次。现在给你目标区域,X表示黑色,O表示白色,问经过怎样的操作可以让得到 这个目标区域。Rx表示把第x行涂成黑色,Cx表示把第x列涂成白色,按操作顺序输出,要求操作数最少,且字典序最小。如果不能得到输出No solution。


思路:

首先我们针对一个点来思考,如果这个点是白色的,那么这个点所在的列一定有涂成白色的操作,而且,这个点所在的行涂成黑色的操作一定在列操作之前。那么我们对于每一个点都能确定一个row i 和col j的关系,形成一个拓扑图。但是,需要考虑一点,如果这个点是白色,不能单纯的就讲这个点所在的列操作后于行操作加入到拓扑图当中,而要考虑,这个点所在的行是否存在黑色结点,如果不存在,那么说明这一行并不需要进行行操作,所以这个点就不产生拓扑关系,对于这个点来说直接列操作就好了。

所以实际的做法就是先记录每一行每一列是否需要进行涂色操作,在这个基础上对这一行或这一列的点统计入度(建立拓扑关系),然后就是用优先队列去维护每个入度为0的点,(之所以用优先队列维护是题目要求字典序最小,当然由于C小于R,所以应该让列操作在前面,我的操作是直接给行数+n),最后判断下有无环就好了。


总结:

这个题我坑在了判断是否有环上,对于有环的图我们肯定不能得到solution,但是我没考虑这个,而是直接看是否存在一开始就有入度为0的点,就wa了,因为即使存在入度为0的点,之后也可能存在环。

另外,n的大小是到500,我在数字转字符串的时候数组开小了,坑了我好久。


代码:


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
char a[505][505];
int b[505][505];
int topo[1005]; //记录每个点的 入度,含义是列数和行数+n
int book[1005]; //记录行和列是否需要操作
char str[1005][5];
int tu[1005][1005];//记录边
int num; //最后的操作数
bool top(int n)
{
    priority_queue<int, vector <int>, greater<int> >a;
    int c[1004];
    int i, j, top=0;
    for(i=0; i<n*2; i++)
    {
//        printf("%d %d\n", book[i], topo[i]);
        if(book[i] && topo[i]==0)
        {
            a.push(i);
        }
    }
    while(!a.empty())
    {
        int x=a.top();
        if(x>=n)
        {

            int y=x-n+1;
            char e[5];
            sprintf(e, "R%d", y);
            strcpy(str[num++], e);
        }
        else
        {
            char e[5];
            sprintf(e, "C%d", x+1);
            strcpy(str[num++], e);
        }
        a.pop();
        if(x>=n)
        {
            for(i=0; i<n; i++)
            {
             if(tu[x][i]) //如果是要求发生在操作x之后
            {
                topo[i]--;
                if(topo[i]==0)
                {
                    a.push(i);
                }
            }
            }
        }
        else
        {
            for(i=n; i<2*n; i++)
            {
             if(tu[x][i])
            {
                topo[i]--;
                if(topo[i]==0)
                {
                    a.push(i);
                }
            }
            }
        }

    }
    for(i=0; i<2*n; i++)if(topo[i])return false;
    return true;
}
int main()
{
    int t;
    while(~scanf("%d", &t))
    {
    while(t--)
    {
        int i, j;
        num=0;
        memset(topo, 0, sizeof(topo));
        memset(book, 0, sizeof(book));
        memset(str, 0, sizeof(str));
        memset(tu, 0, sizeof(tu));
        memset(b, 0, sizeof(b));
        int n;
        cin>>n;
        for(i=0; i<n; i++)scanf("%s", a[i]);
        for(i=0; i<n; i++)
        {
            for(j=0; j<n; j++)
            {
                b[i][j]=a[i][j]=='X';
              if(b[i][j])
                {
                    book[i+n]=1; //记录这一行是否需要操作
                }
                else {
                        book[j]=1;  //记录这一列是否需要操作
                     }
            }
        }
        for(i=0; i<n; i++)
        {
            for(j=0; j<n; j++)
            {
                if(b[i][j])
                {
                    if(book[j]){topo[i+n]++;tu[j][i+n]=1;}//如果这个点所在列需要操作,那么这个点所在行操作应该在列操作之后
                }else
                {
                    if(book[i+n]){topo[j]++;tu[i+n][j]=1;}//如果这个点所在行需要操作,那么这个点所在列操作应该在行操作之后

                }
            }
        }
        bool ans=top(n);
        if(ans==0)printf("No solution\n");
        else
        {
            printf("%s", str[0]);
            int i;
            for(i=1; i<num; i++)printf(" %s", str[i]);
            printf("\n");
        }

    }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值