编程题#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计算出最少需要涂画多少块砖,才能使所有砖的颜色都变成黄色。
![](https://d3c33hcgiwev3.cloudfront.net/q_fFQxleEeW4giIAC5XN5A_fed1927db98dbf29b372d890bb2177cd_Screen-Shot-2015-06-22-at-9.15.40-PM.png?Expires=1472515200&Signature=AA431Z4i4aWkdM3BwgBzHCi5fcRiIb7uj1rwSLVrHBb2RylG5dHSVwvZ8gbgsMqgE-2txJJ257NmzhXW~62NxLjP~Kj5QvJVI9UJoZGQe84N1q7vRVH1d0R19dE3mvWwvCPbAR4hCEv~u2NJJt-LPWLpYV36ju2eehtGXtNG2uo_&Key-Pair-Id=APKAJLTNE6QMUY6HBC5A)
输入
第一行是个整数t(1≤t ≤20),表示要测试的案例数。然后是t个案例。每个案例的首行是一个整数n (1≤n ≤15),表示墙的大小。接下来的n行表示墙的初始状态。每一行包含n个字符。第i行的第j个字符表示位于位置(i,j)上的砖的颜色。“w”表示白砖,“y”表示黄砖。
输出
每个案例输出一行。如果Bob能够将所有的砖都涂成黄色,则输出最少需要涂画的砖数,否则输出“inf”。
样例输入
样例输出
思路分析:这个问题和熄灯问题比较类似,不过就是相比熄灯问题多了个数量的统计,并且还需要判断解中数量最少的;
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;
}