POJ 1753

  • Post author:
  • Post category:其他


1753 题是一个基本的枚举算法的题,基本解题思路就是暴力枚举,但是我们需要知道即便是暴力枚举,也是需要认真分析问题的。

题意:有一个4*4的棋盘,棋盘上有黑白格,每一次你可以翻其中的一个格子。一个格子(x,y)如果被翻,它相邻的前后左右四个格子(如果在棋盘上)也要翻转。现在给你一个初始的棋盘状态,问把这个棋盘翻转到全黑或全白的最少次数;若不能达到全黑或全白,输出Impossible。

思路:只有4*4的棋盘,同时格子只有黑白两面。对于同一个格子,翻两次和不翻没有区别。同时,我们会发现,假如第一行的状态已经确定,那么剩下的行的翻法其实也是确定了的。这样我们从原来需要枚举65536种,到现在只需要枚举第一行16种即可,所以,我们只需要按照求4个数的子集的方式,枚举第一行的翻法即可枚举所有可能形式。

所以,我们需要一个子集递归方法,一个步数求解方法,还有小方法例如判断当前是否全黑或全白、将当前棋子性质翻转等,即可实现枚举。

#include <stdio.h>
#include <iostream>
#include <string>
using namespace  std;
//成功
//暴力枚举,通过枚举第一行元素的组合方式进行暴力搜索
string s[4];//输入矩阵
string tmp[4];//当前棋子状态矩阵
bool b[4];//判断枚举,1-4的子集生成
int foot=100;//步子数
void back2(int x,int y){//将tmp中的元素性质翻转,例如b转成w,w转成b
    if(tmp[x][y]=='b')tmp[x][y]='w';
    else
        tmp[x][y]='b';
}
bool test(){//判断输入矩阵是否直接为全黑或者全白
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(s[i][j]!=s[0][0])return false;
        }
    }
    return true;
}
bool test2(){//判断当前棋子矩阵是否直接为全黑或者全白
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(tmp[i][j]!=tmp[0][0])return false;
        }
    }
    return true;
}
void genera(char flag){//将当前棋子矩阵变为全黑或者全白的步数
    int root=0;
    for(int i=0;i<4;i++){//将当前状态初始化为输入状态
        for(int j=0;j<4;j++){
            tmp[i][j]=s[i][j];
        }
    }
    for(int i=0;i<4;i++){//根据第一行的子集进行转换,即改变第一行的状态
        if(b[i]){
            root++;
            back2(0,i);
            back2(1,i);
            if(i<=2)back2(0,i+1);
            if(i>=1)back2(0,i-1);
        }
        
    }
        for(int i=0;i<4;i++){//根据第一行改变第二行
        if(tmp[0][i]!=flag){
            root++;
            tmp[0][i]=flag;
            back2(1,i);
            if(i<=2)back2(1,i+1);
            if(i>=1)back2(1,i-1);
            back2(2,i);
            
        }
    }
    for(int j=2;j<4;j++){//改变第三和第四行
    for(int i=0;i<4;i++){
        if(tmp[j-1][i]!=flag){
            root++;
            tmp[j-1][i]=flag;
            back2(j,i);
            if(i<=2)back2(j,i+1);
            if(i>=1)back2(j,i-1);
            if(j<=2)back2(j+1,i);
            
        }
    }
    }
    if(test2()&&root<foot){//如果当前棋子为全黑或全白则改变最小步数
        foot=root;
    }
    
}
void gen(int y){//第一行的全部子集
    if(y==4){
        genera('b');//全黑
        genera('w');//全白
    }
    else{
        b[y]=true;//使用位向量法构造第一行的全部子集数
        gen(y+1);
        b[y]=false;
        gen(y+1);
    }
}
int main(){
    for(int i=0;i<4;i++){
        cin>>s[i];
     b[i]=false;
    }
    if(test())
        cout<<0<<endl;//判断当前是否为已经排列好的矩阵
    else
    {//构造矩阵
        gen(0);
        if(foot!=100){
            cout<<foot<<endl;
        }
        else
            cout<<"Impossible"<<endl;//如果无法生成则输出
    }
    return 0;
}



版权声明:本文为Joaniee原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。