zoukankan      html  css  js  c++  java
  • 【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;
    }
    
    个人博客地址: www.moyujiang.com 或 moyujiang.top
  • 相关阅读:
    ThinkPHP整合Kindeditor多图处理示例
    KindEditor用法介绍
    MySQL 1064 错误
    Nginx中虚拟主机与指定访问路径的设置方法讲解
    AJAX PHP无刷新form表单提交的简单实现(推荐)
    教PHP程序员如何找单位(全职+实习),超有用啊!
    利用正则表达式实现手机号码中间4位用星号(*)
    PHP项目做完后想上线怎么办,告诉你免费上线方法!
    备战NOIP——模板复习16
    备战NOIP——STL复习1
  • 原文地址:https://www.cnblogs.com/moyujiang/p/11167749.html
Copyright © 2011-2022 走看看