D. Big Brush (思维,搜索)

题目

https://codeforces.com/contest/1638/problem/D
在这里插入图片描述
大致题意就是:给一个 nm 的网格,要求用 22 的刷子刷最多 n*m 次将它刷成目标颜色状态。若不能则输出 -1.。

思路

逆时过来想:从最后一刷开始,那肯定是完整的 22 的同色田字A。那么紧贴着这个 22 的最后一次刷田字B,就应该满足除了原来A中的色块,其他色块颜色相同,以此类推,紧贴着 A,B 的最后一次刷田字 C ,就应该除了AB刷过的块,其他色块颜色相同……
那么就有了一个刷法:起初,先把图案中所有同色“田字”刷了作为最后一步(他们互不干扰),然后把刷了的块做上标记。之后一直找其他“田字”中除了做了标记的块,其他块颜色相同的田字,依次作为往前的步骤。
具体实现,用栈来存答案。找田字用队列广搜:一开始扫完同色田都加入一个队列,每次弹出队首并检查与其相邻的没用过的田字是否符合要求,若符合要求则将其加入队列。
这个方法一个田字涂色一次就够了,(换个方向,每加一个田字,都至少有一个色块涂好色),所以是满足步数要求的。
至于判断是否有解,只要看涂到不能涂之后,是不是全部色块都涂上了颜色就行了。

代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1005;

int n,m;
int a[MAXN][MAXN];
int used[MAXN][MAXN];    // 记录以这个点为左上角的田字是否用过了
int done[MAXN][MAXN];    // 记录这个色块是否涂好色了
pair<int, int> Q[MAXN*MAXN];  // 存放筛选出符合条件的田字用于搜索
int Ql, Qr;

pair<pair<int, int>, int> ans[MAXN*MAXN];
int ansp;

const int fx[9][2]={{0,0},{1,0},{0,1},{1,1}, {-1,-1},{-1,0},{-1,1},{0,-1},{1,-1}};

// 检查以这个点为左上角的田字是否符合
// 若不符合,返回 -1
// 若全是涂好的点,返回 0
// 若符合且有没涂好的点(即没涂的点颜色一样),返回颜色
int check(int x, int y)
{
    int tmp=0;
    for (int i=0,nx,ny; i<4; i++) {
        nx=x+fx[i][0];
        ny=y+fx[i][1];
        if (!done[nx][ny]) {
            if (tmp==0)
                tmp=a[nx][ny];
            else if (tmp!=a[nx][ny])
                return -1;
        }
    }
    return tmp;
}

void solve()
{
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++) {
        for (int j=1; j<=m; j++)
            scanf("%d",&a[i][j]);
    }

    ansp=0;
    Qr=0;
    Ql=1;
    for (int i=1; i<n; i++) {
        for (int j=1; j<m; j++)
            if (check(i,j)>=0) {
                Q[++Qr]=make_pair(i,j);
                used[i][j]=1;
            }
    }

    while (Ql<=Qr) {
        auto[x,y]=Q[Ql];
        Ql++;
        int chk=check(x,y);
        if (chk>0) {  // 田字内还有块没涂才将这个田字压入答案栈并为块打标记
            for (int i=0; i<4; i++)
                done[x+fx[i][0]][y+fx[i][1]]=1;
            ans[++ansp]=make_pair(make_pair(x,y), chk);
        }
        for (int i=1; i<=8; i++) {
            int nx=x+fx[i][0];
            int ny=y+fx[i][1];
            if (nx>0 && nx<n && ny>0 && ny<m && !used[nx][ny] && check(nx,ny)>=0) {
                Q[++Qr]=make_pair(nx,ny);
                used[nx][ny]=1;
            }
        }
    }
    
    for (int i=1; i<=n; i++) {
        for (int j=1; j<=m; j++)
            if (!done[i][j]) {
                puts("-1");
                return;
            }
    }

    printf("%d\n",ansp);
    for (int i=ansp; i>=1; i--) {
        printf("%d %d %d\n",ans[i].first.first, ans[i].first.second, ans[i].second);
    }
}



int main()
{
    solve();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值