画家问题


编程题#1: 画家问题

来源: POJ (Coursera声明:在POJ上完成的习题将不会计入Coursera的最后成绩。)

注意: 总时间限制: 1000ms 内存限制: 65536kB

描述

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

输入

第一行是个整数t(1≤t ≤20),表示要测试的案例数。然后是t个案例。每个案例的首行是一个整数n (1≤n ≤15),表示墙的大小。接下来的n行表示墙的初始状态。每一行包含n个字符。第i行的第j个字符表示位于位置(i,j)上的砖的颜色。“w”表示白砖,“y”表示黄砖。

输出

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

样例输入


          
          
1
2
3
4
5
6
7
8
9
10
11
2
3
yyy
yyy
yyy
5
wwwww
wwwww
wwwww
wwwww
wwwww

样例输出


          
          
1
2
0
15

思路分析:这个问题和熄灯问题比较类似,不过就是相比熄灯问题多了个数量的统计,并且还需要判断解中数量最少的;

1、定义两个全局数组,按题目要求的最大数行多加一,列多加二;

2、定义猜解函数,因为需要统计最小值,所以在非解的情况返回一个绝对比解大的数;

3、如果为解则需统计此解中所以的数量;

4、在调用函数中需要枚举完成所有解,因为是模拟二进制的进位法,所以就利用墙第一行 wallSize 后面一位的进位情况来确定,枚举的结束,并在此间不断比较以寻找最小值;

代码如下:

 

#include <iostream>

using namespace std;

 

int wallC[16][17] = { 0 },paint[16][17] = { 0 };

//wallC 记录墙的颜色,y为黄色,w为白色,将其转为w 为1,y 为0;

//paint记录需要上色的位置,1表示需要上色,0表示不需要上色

int paintGuess(const int& wSize)

{

    for (int i = 1; i < wSize; i++)

        for (int j = 1; j <= wSize; j++)

        {

            paint[i + 1][j] =(wallC[i][j] + paint[i][j] + paint[i - 1][j] + paint[i][j + 1] + paint[i][j -1]) % 2;

        }

    // 枚举第一行的不同状态组合,再以第一行不同状态组合来确定对应位置是否需要paint

    // 因为相邻的两的位置的paint会相互抵消,所以需要以相邻位置的paint与当前颜色来判断

    // 是否需要按下

    for (int i = 1; i <= wSize; i++)

    {

        int n = (paint[wSize][i] + paint[wSize - 1][i] + paint[wSize][i + 1] + paint[wSize][i - 1]) % 2;

        if (n != wallC[wSize][i])

        {

            return 1001;

        }

                     //paint的最小值,所以返回一个大数,然后比较更新得到最小值

    }

    //判断最后一行是否都已经变成黄色,如果不是则说明这不是解

    int times = 0;

    //统计解的paint的次数

    for (int i = 1; i <= wSize; i++)

        for (int j = 1; j <= wSize; j++)

        {

            if (paint[i][j] == 1)

                times++;

        }

    return times;

}

int enumerate(const int& wSize)

{

    int times = 1001;

    int steps = 0, n = 1;

    while (paint[1][wSize + 1] < 1)         // 据二进制进位规则可知,如果wSize的后一位如果为1的话,则代表wSize位的所有组合都已经进行完成了

    {

        steps = paintGuess(wSize);

        if (steps < times) times =steps;   //求出最小值

        paint[1][1]++;

        n = 1;

        while (paint[1][n] > 1)             //模拟二进制进位

        {

            paint[1][n] = 0;

            n++;

            paint[1][n]++;

        }

    }

    return times;

}

int main(void)

{

    int cases, wSize, r, c;

    char color;

    cin >> cases;

    for (int i = 0; i < cases; i++)

    {

        for ( r = 0; r<16; r++)

            for (c = 0; c < 17; c++)

            {

                wallC[r][c] = 0;

                paint[r][c] = 0;

            }                                  // 为良好习惯,清空上次数据

        cin >> wSize;

        for (r = 1; r <= wSize; r++)

            for (c = 1; c <= wSize; c++)

            {

                cin >> color;

                wallC[r][c] = (color== 'y' ? 0 : 1);

            }

        int times = enumerate(wSize);

        if (times > 1000)

            cout << "inf" << endl;

        else

            cout << times<< endl;

    }

    return 0;

}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值