POJ 1681 Painter's Problem(高斯消元)

Posted ITAK

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ 1681 Painter's Problem(高斯消元)相关的知识,希望对你有一定的参考价值。

传送门


Painter‘s Problem
Time Limit: 1000MS
Memory Limit: 10000K
Total Submissions: 5585
Accepted: 2697

Description

There is a square wall which is made of n*n small square bricks. Some bricks are white while some bricks are yellow. Bob is a painter and he wants to paint all the bricks yellow. But there is something wrong with Bob‘s brush. Once he uses this brush to paint brick (i, j), the bricks at (i, j), (i-1, j), (i+1, j), (i, j-1) and (i, j+1) all change their color. Your task is to find the minimum number of bricks Bob should paint in order to make all the bricks yellow. 
技术分享

Input

The first line contains a single integer t (1 <= t <= 20) that indicates the number of test cases. Then follow the t cases. Each test case begins with a line contains an integer n (1 <= n <= 15), representing the size of wall. The next n lines represent the original wall. Each line contains n characters. The j-th character of the i-th line figures out the color of brick at position (i, j). We use a ‘w‘ to express a white brick while a ‘y‘ to express a yellow brick.

Output

For each case, output a line contains the minimum number of bricks Bob should paint. If Bob can‘t paint all the bricks yellow, print ‘inf‘.

Sample Input

2
3
yyy
yyy
yyy
5
wwwww
wwwww
wwwww
wwwww
wwwww

Sample Output

0
15

Source


题目大意:

一个n*n 的方格 ,我们对它进行染色,每个格子都 可以 染成 白色和黄色,( 一旦我们对这个格子染色 ,他的上下左右 都将改变颜色);给定一个初始状态 , 求将 所有的 格子 染成黄色 最少需要染几次?  若 不能 染成 输出 "inf"

解题思路:

我们可以首先来构造一个矩阵,这个矩阵式干啥的呢,我们可以认为这个矩阵是按下一个格子之后它能够作用的范围,将能够作用的范围用1表示否则用0表示。来举个例子

就 根据一个3*3的矩阵,对于第一行第一列的元素来说,它可以影响的范围是它自己 还有它的右面的值和它的下面的值,矩阵中其余的值都是0,也就是说

A(0,0)= 
1 1 0 
1 0 0 
0 0 0 

A(0,1)= 
1 1 1 
1 0 0 
0 0 0 

现在我们设一个L矩阵,表示初始的状态,如果是y就是0,否则就是1,也就是说我们需要将当前的矩阵操为全是0的矩阵

L + x(1,1)*A(1,1) + x(1,2)*A(1,2) + x(1,3)*A(1,3) + x(2,1)*A(2,1) + ... + x(3,3)*A(3,3) = 0 (1)

上述 方程的 x 表示的是一个未知数,因为我们不知道是否是按下这个按钮。那么x(i, j)=0表示不按,否则表示按。那么我们现在就是求一个这样的方程解最小的x,那么上述方程中的矩阵A可以用一个比较大 的矩阵n*n的来表示,然后就是转换一下关系就行了(具体在代码中有体现)。那么现在就是n个未知数,n个方程,在(1)中,可以两边加上L,那么就是变成了:

x(1,1)*A(1,1) + x(1,2)*A(1,2) + x(1,3)*A(1,3) + x(2,1)*A(2,1) + ... + x(3,3)*A(3,3) = L(2)

X * A = L(类似这样的)

然后我们在枚举一下所有的状态:我们首先要枚举的是自由变元的个数,我们对它的所有状态都进行枚举,然后得到了如果符合状态的话就进行自由变元的赋值,然后我们在对可以确定的变元进行操作,那么肯定就是高斯中的回代过程,然后在进行判断,我们需要的就是最少的1,也就是最少能够操作的数。

具体还得详见代码:

My Code:

<span style="font-size:18px;">#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
using namespace std;
const int MAXN = 3e2+5;
const int INF = 1e9+5;
int equ, var;///equ个方程 var个变量
int a[MAXN][MAXN];///增广矩阵
int x[MAXN];///解的数目
bool free_x[MAXN];///判断是不是自由变元
int free_xx[MAXN];
int free_num;///自由变元的个数
inline int GCD(int m, int n)
{
    if(n == 0)
        return m;
    return GCD(n, m%n);
}
inline int LCM(int a, int b)
{
    return a/GCD(a,b)*b;
}
int n;
int Gauss()
{
    int Max_r;///当前列绝对值最大的存在的行
    ///col:处理当前的列
    int row = 0;
    int cnt = 0;///自由变元的编号
    for(int col=0; row<equ&&col<var; row++,col++)
    {
        Max_r = row;
        for(int i=row+1; i<equ; i++)///找当前列中最大的行
            if(abs(a[i][col]) > abs(a[Max_r][col]))
                Max_r = i;
        ///交换Max_r行 与 当前行
        if(Max_r != row)
            for(int i=0; i<var+1; i++)
                swap(a[row][i], a[Max_r][i]);

        if(a[row][col] == 0)
        {
            row--;
            free_xx[cnt++] = col;///后面的
            continue;
        }
        for(int i=row+1; i<equ; i++)
        {
            if(a[i][col])
            {
                for(int j=col; j<var+1; j++)
                    a[i][j] ^= a[row][j];
            }
        }
    }
    for(int i=row; i<equ; i++)
        if(a[i][var])
            return -1;///无解
    return var - row;///自由变元的个数
}
int Solve(int S)
{
    int s = (1<<S);///所有的状态
    int ans = INF;
    for(int i=0; i<s; i++)///枚举状态
    {
        int cnt = 0;
        memset(x, 0, sizeof(x));
        for(int j=0; j<S; j++)
        {
            if(i & (1<<j))
            {
                cnt++;///1的个数,也就是能够操作的个数
                x[free_xx[j]] = 1;
            }
        }
        for(int j=var-S-1; j>=0; j--)
        {
            int tmp = a[j][var], tp, ok = 1;
            for(int k=j; k<var; k++)
            {
                if(a[j][k] && ok)
                {
                    tp = k;
                    ok = 0;
                }
                if(a[j][k] && k!=j)
                    tmp ^= x[k];
            }
            x[tp] = tmp;
            cnt += x[tp];
        }
        ans = min(ans, cnt);///最少的操作数
    }
    return ans;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        equ = var = n*n;
        memset(a, 0, sizeof(a));
        for(int i=0; i<var; i++)
        {
            int ta = i%n, tb = i/n;
            a[i][i] = 1;
            if(ta > 0)
                a[i][i-1] = 1;
            if(ta < n-1)
                a[i][i+1] = 1;
            if(tb > 0)
                a[i][i-n] = 1;
            if(tb < n-1)
                a[i][i+n] = 1;
        }
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                char ch;
                cin>>ch;
                if(ch == 'y')
                    a[i*n+j][var] = 0;
                else
                    a[i*n+j][var] = 1;
            }
        }
        int S = Gauss();
        if(S == -1)
            puts("inf");
        else
            cout<<Solve(S)<<endl;
    }
    return 0;
}</span>


















以上是关于POJ 1681 Painter's Problem(高斯消元)的主要内容,如果未能解决你的问题,请参考以下文章

POJ 1681 Painter's Problem(高斯消元)

POJ 1681 Painter's Problem 高斯消元 二进制枚举

poj 1681 Painter&#39;s Problem(高斯消元)

pku_oj: 1681Painter's Problem(画家问题)(C++)

Painter's Problem (高斯消元)

ACM/ICPC 之 枚举(POJ1681-画家问题+POJ1166-拨钟问题)