POJ1753 状态压缩(+BFS) + 棋盘问题

0

棋盘问题,改变一个子的颜色,这个子以及这个子周围的四个位置(左右上下)的子分别变色,求最少的改变次数。

此类题大部分应该可以用状态压缩+暴力搜索解决。纯粹找规律不太合理。

1)第一种方法,状态压缩后BFS暴力搜索。因为棋盘很小,只有16个格子,枚举所有的状态共有2^16=65536种。所以有可以用int数组存储65535个状态用以确认哪一个出现了哪一个没出现,然后暴力枚举+BFS的搜索方式。

2)第二种,或者不进行状态压缩,直接按顺序,确定改变次数为1、改变次数为2...改变次数为16,在每一个确定的改变次数下用DFS求解。(直接DFS是从1到16一条路走到头然后不断回溯,第一次得到的颜色统一的改变次数未必是最小次数,所以改用有些BFS思想的DFS,但因为没有像BFS那样存储状态,所以做了一些重复的工作,时间会比第一种方法慢。)

3)WA,还未调试出来:

因为意识到解题的关键是,每一位置的子改变一次和改变两次其实是一样的&&改变第a个子然后改变第b个子的效果等同于先改变第b个子然后改变第a个子即与顺序无关,所以我设想不用状态压缩,直接按顺序只改变当前子之后的子不断BFS枚举,自然而然就可以避免重复的搜索工作。具体是指,第一轮,依次改变棋盘上第一个、第二个、第十六个子,共十六种情况存入队列,然后第二轮,从队列中取出改变第一个子后的状态,依次尝试改变第一个子往后的子,改变之后存入队列...第x轮,从队列中取出队首即上一轮中改变第m个子后的状态,那么我们只需考虑接下来改变第m个子之后的子即可,而不用考虑改变第m个子之前的子,因为那种情况相当于先改变第m个子之前的子此时改变第m个子。

我觉得我的思想应该没问题,起到的效果是等同于状态压缩中记录状态的作用的...不过就是没AC,样例过,疑问!?

1

1)

AC代码:

#include <iostream>
#include <queue>
#include <string.h>

using namespace std;
//int mmap[5][5]={0};
int dir[5][2]={{-1,0},{1,0},{0,-1},{0,1},{0,0}};
int visted[65536]={0};
int state[16];
int ini_state;
int flag;
int step;

struct boards{
    int state;
    int step;
};
queue<struct boards> q;
void initialize(){//☆这里可以打表
    int temp;
    int x,y;
    for(int i=1;i<=4;i++){
        for(int j=1;j<=4;j++){
            temp=0;
            for(int l=0;l<5;l++){
                x=i+dir[l][0];
                y=j+dir[l][1];
                if(x<1||x>4||y<1||y>4){
                    continue;
                }
                temp^=(1<<( (x-1)*4+y -1 ));
            }
            state[(i-1)*4+j-1]=temp;
        }
    }
}
void Bfs(){
    struct boards cur_board;
    cur_board.state=ini_state;
    cur_board.step=0;
    q.push(cur_board);
    visted[cur_board.state]=1;
    while(!q.empty()){
        cur_board=q.front();
        q.pop();
        if(cur_board.state==0||cur_board.state==65535){
            step=cur_board.step;
            flag=1;
            return ;
        }
        struct boards next_board;
        for(int i=0;i<16;i++){
            next_board.state=cur_board.state^state[i];//☆这里可以马上判断一步,如果颜色统一立即return
            if(!visted[next_board.state]){
                next_board.step=cur_board.step+1;
                q.push(next_board);
                visted[next_board.state]=1;
            }
        }
    }
    return ;
}
int main(){
    ///initial
    ini_state=0;
    flag=0;
    step=0;
    char check;
    for(int i=1;i<=4;i++){
        for(int j=1;j<=4;j++){
            cin>>check;
            if(check=='b')
                ini_state+=(1<< ( (i-1)*4+j - 1) );
            //cin>>mmap[i][j];
        }
    }
    //cout<<ini_state<<endl;
    initialize();//16 states of while board when only the piece was placed on the board.
    //for(int i=0;i<=15;i++){ cout<<state[i]<<endl;}

    ///bfs
    Bfs();

    ///result
    if(flag)
        cout<<step<<endl;
    else{
        cout<<"Impossible"<<endl;
    }
}

2)

AC代码:

#include <iostream>
#include <string.h>

using namespace std;
int mmap[5][5];
int dir[5][2]={{0,-1},{-1,0},{0,1},{1,0},{0,0}};//左 上 右 下 中
//int dir[5][5]={{-1,0},{1,0},{0,-1},{0,1},{0,0}};
int sum;
int flag=0;
int Judge(){
    for(int i=1;i<=4;i++){
        for(int j=1;j<=4;j++){
            if(mmap[i][j]!=mmap[1][1]){
                return 0;
            }
        }
    }
    return 1;
}

void flip(int x,int y){
    for(int i=0;i<=4;i++){
        int x_next=x+dir[i][0];
        int y_next=y+dir[i][1];
        if(1<=x_next&&x_next<=4&&1<=y_next&&y_next<=4){
            mmap[x_next][y_next]=!mmap[x_next][y_next];
        }
    }
}

void Dfs(int x,int y,int obj,int cur_step){

    if(cur_step==obj){
       flag=Judge();
       return;
    }

    if(flag||x==5){
        return ;
    }

    flip(x,y);
/*
    if(y<4){
        Dfs(x,y+1,obj,cur_step+1);
    }
    else{
        Dfs(x+1,1,obj,cur_step+1);
    }
*/

    if(y+1<=4&&x<=4)
        Dfs(x,y+1,obj,cur_step+1);
    else if(y==4){
        Dfs(x+1,1,obj,cur_step+1);//不能写else if(y==4&&x+1<=4)因为判断函数(当cur_step==obj在dfs函数的开头,所以每一次翻转棋子之后,都应该调用dfs让其判断一次,如果因为x、y分别是最后一行最后一列了,因为x+1>4&&y==4而没有能进dfs直接把棋子翻转回来,return回到上一层,是不公平的,相当于这一轮对于最后一行最后一列的棋子翻转后没有进行判断是否是统一颜色了!!)
    }

    flip(x,y);//一定注意!不用bfs,而是dfs,每次改变的东西当return回来之后一定要状态还原!
/*
    if(y<4){
        Dfs(x,y+1,obj,cur_step);
    }
    else{
        Dfs(x+1,1,obj,cur_step);
    }
*/

    if(y+1<=4&&x<=4)
        Dfs(x,y+1,obj,cur_step);
    else if(y==4){
        Dfs(x+1,1,obj,cur_step);
    }

    return ;
}

int main(){
    memset(mmap,0,sizeof(mmap));
    char check;
    for(int i=1;i<=4;i++){
        for(int j=1;j<=4;j++){
            cin>>check;
            if(check=='b'){
                mmap[i][j]=1;//b 1,w 0
            }
        }
    }
    int step=0;//当前翻转棋子个数
    for(int l=0;l<=16;l++){//应该翻转的棋子个数
       Dfs(1,1,l,step);
        if(flag==1){
            cout<<l<<endl;
            break;
        }
        if(l==16){
            cout<<"Impossible";
        }
    }
    return 0;
}

3)

WA代码第一份:

#include <iostream>
#include <queue>

using namespace std;
int sum;
int temp=0;
struct boards{
    char chess[4][4];
    int sum;
    int x_past;
    int y_past;
};
int dir[4][4]={{0,-1},{-1,0},{0,1},{1,0}};//左上右下
 queue <struct boards> q;
int Judge(struct boards boards_judge){

    char now=boards_judge.chess[0][0];
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(boards_judge.chess[i][j]!=now){
                return 0;
            }
        }
    }
    return 1;
}

int Reserve(){
    struct boards boards_cur;
    struct boards boards_future;
    if(q.empty()) return -1;
    boards_cur =q.front();

    q.pop();
    for(int i=boards_cur.x_past;i<4;i++){
        for(int j=boards_cur.y_past;j<4;j++){
            int x=i,y=j;
            if(x<0||x>3||y<0||y>3) continue;
            if(x==boards_cur.x_past&&y==boards_cur.y_past) continue;

            boards_future=boards_cur; //可以直接赋值
            boards_future.sum=boards_cur.sum+1;
            boards_future.x_past=i;
            boards_future.y_past=j;
            //cout<<"note:"<<boards_cur.sum<<endl;

            if(boards_cur.chess[i][j]=='b'){
                boards_future.chess[i][j]='w';
            }
            else{
                boards_future.chess[i][j]='b';
            }
            for(int l=0;l<4;l++){
                x=i+dir[l][0];
                y=j+dir[l][1];
                if(x<0||x>3||y<0||y>3) continue;
                if(boards_cur.chess[x][y]=='b'){
                    boards_future.chess[x][y]='w';
                }
                else{
                    boards_future.chess[x][y]='b';
                }
            }

            if(Judge(boards_future)){
                sum=boards_future.sum;
                return 1;
            }
            q.push(boards_future);
            //boards_cur.sum--;//!假如没有boards_future,直接q.push(boards_cur),然后再对boards_cur操作,是否,放入队列的就不再受影响----是的!不受影响

        }
    }
    return 0;
}
int main()
{
    struct boards board;
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            cin>>board.chess[i][j];
        }
    }
    board.sum=0;
    sum=0;
    board.x_past=-1;
    board.y_past=-1;
    q.push(board);
    if(Judge(board)){
    }
    else{
        while(1){//0-Lost 1-Win
            //temp+=1;
            int state=Reserve();
            if(state==1){
                break;
            }
            if(state==-1){
                sum=-1;
                break;
            }
        }
    }
    if(sum!=-1)
        cout<<sum<<endl;
    else{
        cout<<"Impossible"<<endl;
    }
}

WA代码第二份

#include <iostream>
#include <string.h>

using namespace std;
int mmap[4][4];
int dir[5][2]={{0,-1},{-1,0},{0,1},{1,0},{0,0}};//左 上 右 下 中
int sum;
int flag=0;
int Judge(){

    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(mmap[i][j]!=mmap[1][1]){
                return 0;
            }
        }
    }
    return 1;
}
void flip(int x,int y){
    for(int i=0;i<=4;i++){
        int x_next=x+dir[i][0];
        int y_next=y+dir[i][1];
        if(0<=x_next&&x_next<=3&&0<=y_next&&y_next<=3){
            mmap[x_next][y_next]=!mmap[x_next][y_next];
        }
    }
}
void Dfs(int x,int y,int obj,int cur_step){
     if(flag)
        return ;
    if(cur_step==obj){
        if(Judge()){
            flag=1;
            sum=cur_step;
        }
        else {
            return ;
        }
    }

    for(int i=x;i<4;i++){
        for(int j=y;j<4;j++){
            if(i==x&&j==y){
                continue;
            }
            if(i<0||i>3||j<0||j>3){
                continue;
            }
            flip(i,j);

            Dfs(i,j,obj,cur_step+1);

            flip(i,j);
        }
    }
    return ;
}
int main(){
    memset(mmap,0,sizeof(mmap));
    char check;
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            cin>>check;
            if(check=='b'){
                mmap[i][j]=1;//b 1,w 0
            }
        }
    }
    int step=0;//当前翻转棋子个数
    for(int l=0;l<=16;l++){//应该翻转的棋子个数
        Dfs(0,-1,l,step);
        if(flag==1){
            cout<<sum<<endl;
            break;
        }
        if(l==16){
            cout<<"Impossible";
        }
    }
    return 0;
}
时间: 2024-10-09 15:45:56

POJ1753 状态压缩(+BFS) + 棋盘问题的相关文章

hdu5094 状态压缩+bfs

http://acm.hdu.edu.cn/showproblem.php?pid=5094 Problem Description This story happened on the background of Star Trek. Spock, the deputy captain of Starship Enterprise, fell into Klingon's trick and was held as prisoner on their mother planet Qo'noS.

胜利大逃亡(续)(状态压缩bfs)

胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7357    Accepted Submission(s): 2552 Problem Description Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带

状态压缩动态规划 -- 棋盘问题 POJ 1321

一个 N * N 的棋盘上面,有些格子不能放,放置 M 的棋子, 每两个棋子不能在同一行或者同一列,问有多少种放法 DFS太慢,用SCR好点点 Python 只有 22 行,其实可以更短,但是得排成很长很长的一行 while True: table = [ [ 0 for j in range( 300 ) ] for i in range( 12 ) ] table[0][0] = 1 boardsize, chessnum = map( int, raw_input().split() )

2014 Super Training #6 G Trim the Nails --状态压缩+BFS

原题: ZOJ 3675 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3675 由m<=20可知,可用一个二进制数表示指甲的状态,最多2^20,初始状态为0,表示指甲都没剪,然后BFS找解,每次枚举剪刀的两个方向,枚举移动的位数进行扩展状态即可. 代码: #include <iostream> #include <cstdio> #include <cstring> #include &

hdu1429胜利大逃亡(续) (状态压缩+BFS)

Problem Description Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)-- 这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带锁的门,钥匙藏在地牢另外的某些地方.刚开始Ignatius被关在(sx,sy)的位置,离开地牢的门在(ex,ey)的位置.Ignatius每分钟只能从一个坐标走到相邻四个坐标中的其中一个.魔王每t分钟回地牢视察一次,若发现Ignatius不在原位置便把他拎回去.经过若干次的尝试,Ignatius已画

hdu 4771 Stealing Harry Potter&#39;s Precious (状态压缩+bfs)

Stealing Harry Potter's Precious Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1297    Accepted Submission(s): 619 Problem Description Harry Potter has some precious. For example, his invisib

hdu1885Key Task(状态压缩+bfs)

题目链接: 啊哈哈,点我点我 这题和hdu1429是姊妹题  请参见传送门 题目: Key Task Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1187    Accepted Submission(s): 470 Problem Description The Czech Technical University is rath

hdu1429胜利大逃亡(续)(状态压缩+bfs)

题目链接: 啊哈哈,点我点我 题意及思路 最开始我以为跟普通的bfs一样,所以直接写了一个朴素的bfs,一跑,前两组数据对了,但是第三组不对,一看,走过的还可以走啊,所以不能标记,结果我的bfs乱改,最后 毫无疑问改成了死循环.所以看题解... 思路:因为有10中不同的钥匙,每种都有两种状态,所以结合计算机是二进制保存的特点,刚好把这10把钥匙当成每一个为,要要1<<10个位保存所有的状态,然后就是模拟捡起钥匙,捡起钥匙就是说明这个位上的数字变成1这个状态,所以自然而然想到了位运算,只要|一下

hdu 3681 Prison Break(状态压缩+bfs)

Problem Description Rompire is a robot kingdom and a lot of robots live there peacefully. But one day, the king of Rompire was captured by human beings. His thinking circuit was changed by human and thus became a tyrant. All those who are against him