poj 1681 Painter's Problem 高斯消元

题目链接  http://poj.org/problem?id=1681


题意:有一个n*n的正方形,每个区域开始都是白色或者黄色的,现在想把整个区域都刷成黄色,每次都可以选择一个区域,然后这个区域以及和它相邻的四个区域都会同时改变颜色,问最后能不能把所有的区域都刷成黄色,如果可以,输出需要的最少的粉刷次数,如果不能就输出inf。

思路:高斯消元,每个区域的最终的颜色都只和自己的初始颜色,自己和周围的四块区域的一共粉刷次数有关,所以我们可以把每个区域都列出一个方程,然后再用高斯消元来求解整个方程组有没有解,根据题意,我们可以知道,每个区域最多只会刷一次,因为刷两次那么就和没刷一样,所以每个未知数的解就只有0或者1,只要解出来,那么就是最少的粉刷数。其实只要把矩阵写出来,剩下的就是一道模板题了。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;


int mat[250][250];
int n;
int an[250];
char s[250][250];
int x[5]={0,0,0,1,-1};
int y[5]={0,1,-1,0,0};


bool ok(int a,int b){
    if(a<0||a>=n||b<0||b>=n)
        return false;
    return true;
}


int Gauss(){
    int i,j;
    for(i=0,j=0;i<n*n&&j<n*n;i++,j++){
        int k=i;
        for(int p=i+1;p<n*n;p++)
            if(abs(mat[k][j])<abs(mat[p][j])) k=p;
        if(k!=i){
            for(int p=j;p<=n*n;p++)
                swap(mat[i][p],mat[k][p]);
        }
        if(mat[i][j]==0){
            i--;
            continue;
        }


        for(int p=i+1;p<n*n;p++) if(mat[p][j]){
            int lcm=abs(mat[i][j])/__gcd(abs(mat[i][j]),abs(mat[p][j]))*abs(mat[p][j]);
            //cout<<lcm<<' '<<mat[i][j]<<' '<<mat[p][j]<<endl;
            int ta=lcm/abs(mat[p][j]),tb=lcm/abs(mat[i][j]);
            if(mat[i][j]*mat[p][j]<0) tb=-tb;
            for(int g=j;g<=n*n;g++)
                mat[p][g]=(mat[p][g]*ta-mat[i][g]*tb)%2;
        }
    }




    for(int p=i;p<n*n;p++)
        if(mat[p][n*n]) return -1;


    for(int p=n*n-1;p>=0;p--){
        int tem=mat[p][n*n];
        for(int g=p+1;g<n*n;g++)
            tem-=mat[p][g]*an[g];
        an[p]=tem;
        an[p]=(an[p]%2+2)%2;
    }
    int ans=0;
    for(int p=0;p<n*n;p++)
        if(an[p]) ans++;
    return ans;
}


int main()
{
    int cas;
    cin>>cas;
    while(cas--){
        memset(mat,0,sizeof(mat));
        memset(an,0,sizeof(an));
        cin>>n;
        for(int i=0;i<n;i++){
            scanf("%s",s[i]);
            for(int j=0;j<n;j++) if(s[i][j]=='w')
                mat[i*n+j][n*n]=1;
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                for(int k=0;k<5;k++){
                    int xx=i+x[k];
                    int yy=j+y[k];
                    if(ok(xx,yy)) mat[i*n+j][xx*n+yy]=1;
                }
            }
        }
        int num=Gauss();
        if(num==-1)
            cout<<"inf"<<endl;
        else cout<<num<<endl;
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值