【DFS练习】-翻棋子-C++

Description
有一个4*4的棋盘,放有16枚棋子。 
每个棋子都是一面黑一面白,一开始有的黑面朝上,有的白面朝上。 
下面是一个例子,这个例子用文字描述为: 
bwbw 
wwww 
bbwb 
bwwb 
我们可以任选一个棋子,把它自己和它的相邻棋子(上下左右,如果有的话)翻面。 
比如在例子中如果我们选第3行第1列的棋子翻面,布局就变成如下: 
bwbw 
bwww 
wwwb 
wwwb 
题目 
求出能把所有棋子都翻成白色或都黑色的最少的步数。 
Input
4行每行4个字符,可能是b(黑)或w(白) 
Output
一个数,最少步数。如果无解输出Impossible 
Sample Input
bwbw
wwww
bbwb
bwwb
Sample Output
Impossible

这道题目是一道典型的 深搜。我们可以用3个参数记录一种状态:
step,x,y;
step即当前状态已经使用的步数,x,y,记当前正在考虑是否翻面的棋子的坐标,但是把x,y传到下一层有些麻烦…我们需要判断,如果y=4,那么就传给下一层(x+1,1),否则传给下一层(x,y+1),因为这里没有考虑到x是否越界,所以在dfs的拓展之前还要判断if(x==5)return;
代码实现。
代码实现一直是有些麻烦的事情,有些思路不及时打下来的话就可能逐渐消退。
根据题意,搜索的终点是所有棋子都朝向同一面,也就是任意棋子的朝向==第一颗的
那么很轻易可以写下check函数:

bool check()
{
    for(int i=1;i<=4;i++)
    {
        for(int j=1;j<=4;j++)
        {
            if(mp[i][j]!=mp[1][1])return 0;
        }
    }
    return 1;
}

翻面也可以暴力写出来:

int dir[5][2]={{0,0},{0,1},{0,-1},{1,0},{-1,0}};
void fan(int x,int y)
{
    for(int i=0;i<5;i++)
    {
        int tx=x+dir[i][0],ty=y+dir[i][1];
        if(in(tx,ty))mp[tx][ty]=back(mp[tx][ty]);
    }
}

顺便把判断越界的写好

bool in(int x,int y)
{
    return 1<=x&&x<=4&&1<=y&&y<=4;
}

然后就是核心部分—dfs了。
先贴一个模板:

void dfs()//参数用来表示状态  
{  
    if(到达终点状态)  
    {  
        ...//根据题意添加  
        return;  
    }  
    if(越界或者是不合法状态)  
        return;  
    if(特殊状态)//剪枝
        return ;
    for(扩展方式)  
    {  
        if(扩展方式所达到状态合法)  
        {  
            修改操作;//根据题意来添加  
            标记;  
            dfs();  
            (还原标记);  
            //是否还原标记根据题意  
            //如果加上(还原标记)就是 回溯法  
        }  
 
    }  
}  

1.参数
void dfs(int step,int x,int y)
2.终点状态
if(check()){...}
3.不合法状态
if(x==5)return;
4.剪枝
这道题不需要 awa
5.扩展方式
这道题的扩展方式和模板有点不一样,这个只分两种:翻或者是不翻,但是要注意的是,翻之前要给x,y打上标记,翻完了再取消标记,达到回溯的效果!

fan(x,y);
    if(y==4)dfs(step+1,x+1,1);
    else dfs(step+1,x,y+1);
    fan(x,y);
     
    if(y==4)dfs(step,x+1,1);
    else dfs(step,x,y+1);
    return;

输入的时候最好把原图转换成01矩阵,方便后续操作awa

for(int i=1;i<=4;i++)
    {
        for(int j=1;j<=4;j++)
        {
            cin>>ch;
            if(ch=='b')
            {
                mp[i][j]=1;
            }
            else mp[i][j]=0;
        }
    }

那个布尔类型的ok是来记是否有解的,如果没有就输出Impossible就可以了。
完整代码贴一下还是:

#include<bits/stdc++.h>
using namespace std;
char ch;
int mp[5][5],/*01矩阵*/min_ans=0x3f3f3f3f,dir[5][2]={{0,0},{0,1},{0,-1},{1,0},{-1,0}};
bool ok;
int back/*反面*/(int x)
{
    return (x+1)%2;
}
bool in(int x,int y)
{
    return 1<=x&&x<=4&&1<=y&&y<=4;
}
bool check()
{
    for(int i=1;i<=4;i++)
    {
        for(int j=1;j<=4;j++)
        {
            if(mp[i][j]!=mp[1][1])return 0;
        }
    }
    return 1;
}
void fan(int x,int y)
{
    for(int i=0;i<5;i++)
    {
        int tx=x+dir[i][0],ty=y+dir[i][1];
        if(in(tx,ty))mp[tx][ty]=back(mp[tx][ty]);
    }
}
void dfs(int step,int x,int y)
{
    if(check())
    {
        min_ans=min(min_ans,step);
        ok=1;
        return;
    }
    if(x==5)return;
     
    fan(x,y);
    if(y==4)dfs(step+1,x+1,1);
    else dfs(step+1,x,y+1);
    fan(x,y);
     
    if(y==4)dfs(step,x+1,1);
    else dfs(step,x,y+1);
    return;
}
int main()
{
    for(int i=1;i<=4;i++)
    {
        for(int j=1;j<=4;j++)
        {
            cin>>ch;
            if(ch=='b')
            {
                mp[i][j]=1;
            }
            else mp[i][j]=0;
        }
    }
    dfs(0,1,1);
    if(!ok)
    {
        cout<<"Impossible"<<endl;
    }
    else cout<<min_ans<<endl;
    return 0;
}
posted @ 2019-07-07 20:13  摸鱼酱  阅读(268)  评论(0编辑  收藏  举报