画家问题-局部枚举

目录

描述

输入

输出

样例输入

样例输出

思路

代码示范


描述

有一个正方形的墙,由N*N个正方形的砖组成,其中一些砖是白色的,另外一些砖是黄色的。Bob是个画家,想把全部的砖都涂成黄色。但他的画笔不好使。当他用画笔涂画第(i, j)个位置的砖时, 位置(i-1, j)、 (i+1, j)、 (i, j-1)、 (i, j+1)上的砖都会改变颜色。请你帮助Bob计算出最少需要涂画多少块砖,才能使所有砖的颜色都变成黄色。

 

输入

本题有多组测试数据(25组以内),每一组中第一行是一个整数n (1≤ n ≤15),表示墙的大小。接下来的n行表示墙的初始状态。每一行包含n个字符。第i行的第j个字符表示位于位置(i,j)上的砖的颜色。“w”表示白砖,“y”表示黄砖。

输出

输出为一行,如果Bob能够将所有的砖都涂成黄色,则输出最少需要涂画的砖数,否则输出“inf”。

样例输入

5
wwwww
wwwww
wwwww
wwwww
wwwww

样例输出

15

思路

使用暴力算法,局部枚举第一行以确定所有上色可能。

核心:为使上一行颜色恒为黄色,下一行上色情况一定。以此类推,只要对第一行所有情况进行枚举,即可确定整面墙体上色情况。

1.封装函数judge():用于判断该第一行情况下是否能完成整墙上色。

  • 算法思路:先置第一行上色情况。
  • 根据上一行上色情况判断本行是否上色。依次计算每行上色。
  • 判断该上色思路能否达成整墙上色。

2.封装函数enumerate():

  • 枚举思路:每格砖只有上色与否两种可能,用二进制表示。

 3.考虑到连续测试,每次开始务必初始化上色统计。

代码示范

#define _CRT_SECURE_NO_WARNINGS    //所有头文件和宏定义为个人爱好
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <string.h>
#include <math.h>
#include <cmath>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#define maxf 16
using namespace std;

int judge(int(*ori)[maxf], int(*paint)[maxf], int n)
{
    for (int i = 0; i < n-1; i++)
    {
        for (int j = 0; j < n; j++)
        {
            paint[i + 1][j] = ori[i][j] + paint[i][j];    //根据上一行情况统计本行上色情况
            if (i - 1 > -1)paint[i + 1][j] += paint[i - 1][j];
            if (j - 1 > -1)paint[i + 1][j] += paint[i][j - 1];
            if (j + 1 < n)paint[i + 1][j] += paint[i][j + 1];
            paint[i + 1][j] %= 2;
        }
    }
    for (int j = 0; j < n; j++)
    {
        int flag=paint[n - 2][j] + paint[n - 1][j];    //判断在上一行限制下,最后一行能否转为黄色
        if (j + 1 < n)flag += paint[n - 1][j+1];
        if (j - 1 > -1)flag += paint[n - 1][j - 1];
        flag %= 2;
        if (flag != ori[n - 1][j])return maxf * maxf;    //原色为白,不上色;原色为黄,被迫上色均为上色失败
    }
    int step = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)step += paint[i][j];    //统计上色次数
    }
    return step;
}
int enumerate(int (*ori)[maxf], int(*paint)[maxf], int n)
{
    int minc = maxf * maxf;
    int step = 0;
    while (!paint[0][n])    //二进制溢出,枚举完成
    {
        step = judge(ori, paint, n);
        if (step < minc)minc = step;
        paint[0][0]++;    //设置第一行情况
        int cnt = 0;    //进行二进制算法,从低位开始检查
        while (paint[0][cnt] > 1)    //逢二进一
        {
            paint[0][cnt] = 0;
            paint[0][++cnt]++;
        }
    }
    return minc;
}
int main()
{
    int n;
    while (cin >> n)
    {
        int ori[maxf][maxf];
        int paint[maxf][maxf];
        for (int i = 0; i < n; i++)
        {
            char c=getchar();    //输入墙面原始色彩
            for (int j = 0; j < n; j++)
            {
                c = getchar();
                if (c == 'w')ori[i][j] = 1;    //需要上色
                else ori[i][j] = 0;
                paint[i][j] = 0;    //初始化上色情况
            }
        }
        getchar();
        paint[0][n] = 0;    //初始化枚举情况
        int step = enumerate(ori, paint, n);
        if (step == maxf * maxf)cout << "inf" << endl;
        else cout << step << endl;
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值