zoukankan      html  css  js  c++  java
  • 基础搜索算法的常见题型

    重点:这是一份课件整理,出自杨乐大佬之手,就此声明。

    首先,搜索是一种暴力,在数据范围小的情况下,枚举所有的可能性。我们来模拟处理问题的步骤。

    搜索主要分两类:dfs(深度优先搜索)

           :bfs(广度优先搜索)

    dfs的经典例题:

    范式:

    void dfs(_position_,_state_)
    if _success_ then _goal_
    else
        for every possible _move_//枚举每一种可能行动
        if _acailable_ then//如果没有违法约束
           _new_state_=(_state_,_move_)//计算新的状态
           _new_possible_=nexr_position_ //计算下一个阶段
           dfs(_new_position_,_new_state_)//进行下一步的搜索

    1.数独问题

    题目梗概:给出一个 9×9 的棋盘,上面已经填好了部分数字。要 求补充完整个棋盘,并且使得每行、每列、每个 3×3 小方阵内没 有重复的数字。
    * 分析数独问题: 1 “划分游戏阶段”:从左到右从前到后,决定每一个空格的数字

             2“行动”:对于某一个特定的空格,枚举上面填什么数字

             3* “胜利”:全部填完了之后,判断这个填的方法是否符合条件

    #include<iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N = 9;
    const int Group[9][9] = 
    {
        0, 0, 0, 1, 1, 1, 2, 2, 2,
        0, 0, 0, 1, 1, 1, 2, 2, 2,
        0, 0, 0, 1, 1, 1, 2, 2, 2,
        3, 3, 3, 4, 4, 4, 5, 5, 5,
        3, 3, 3, 4, 4, 4, 5, 5, 5,
        3, 3, 3, 4, 4, 4, 5, 5, 5,
        6, 6, 6, 7, 7, 7, 8, 8, 8,
        6, 6, 6, 7, 7, 7, 8, 8, 8,
        6, 6, 6, 7, 7, 7, 8, 8, 8
    };
    
    int a[N][N];
    int row[N][N], col[N][N], gr[N][N];
    
    
    void print()
    {
        printf("One Possible Solution:
    ");
        for(int i=0; i<N; i++)
        {
            for(int j=0; j<N; j++) printf("%d ", a[i][j] + 1);
            printf("
    ");
        }
    }
    void dfs3(int x, int y)
    {
        if(x == N)
        {
            print();
            return;
        }
    
        int next_x = x, next_y = y + 1;
        if(next_y == N) next_x = x + 1, next_y = 0;//提前将下一步的位置处理好 
    
        if(a[x][y] >= 0) dfs3(next_x, next_y);//已经填好数了,那么我就跳过 
        else
        {
            for(int i=0; i<N; i++)
                if(!row[x][i] && !col[y][i] && !gr[Group[x][y]][i])
                {
                    row[x][i] = col[y][i] = gr[Group[x][y]][i] = 1;
    
                    a[x][y] = i;//这个数没有出现过,就让它等于当前数 并标记 
                    dfs3(next_x, next_y);
                    a[x][y] = -1;
    
                    row[x][i] = col[y][i] = gr[Group[x][y]][i] = 0;
                }
                /*每到一个位置,枚举每一个数,如果该数行,列,小正方都不重复,则填入
                继续搜索下一个,之后便是回溯一步*/ 
        }
    }
    
    int main()
    {
    
    
        for(int i=0; i<N; i++)
            for(int j=0; j<N; j++)
                scanf("%d", &a[i][j]), a[i][j]--;//因为我们从0开始存 
    
        printf("Dfs Method3:
    ");
    
        for(int i=0; i<N; i++)
            for(int j=0; j<N; j++)
                if(a[i][j] >= 0) 
                    {
                        row[i][a[i][j]] = 1;
                        col[j][a[i][j]] = 1;
                        gr[Group[i][j]][a[i][j]] = 1;
                    }
            /*把每一行每一列,每一小方块的已经给出的数全部标记为,
            而这个group就是来判定这个位置是属于哪一个小方块的*/
    /*        printf("row:
    ");
        for(int i=0; i<N; i++)
        {
            for(int j=0; j<N; j++) printf("%d ", row[i][j]);
            printf("
    ");
        }
        printf("********************
    ");
            printf("col:
    ");
        for(int i=0; i<N; i++)
        {
            for(int j=0; j<N; j++) printf("%d ", col[i][j]);
            printf("
    ");
        }
        printf("********************
    ");
            printf("gr:
    ");
        for(int i=0; i<N; i++)
        {
            for(int j=0; j<N; j++) printf("%d ", gr[i][j]);
            printf("
    ");
        }
        printf("********************
    ");    
        如果对col,row,gr的定义还是不大懂的话,可以解锁以上程序加以理解*/
        dfs3(0, 0);
    
        return 0;
    }

    * 分析背包问题: 1 “划分游戏阶段”:从第一个物品开始,考虑它的选取情况

             2 “行动”:对于某一个特定的物品,考虑它选不选

             3* “约束”:选取完成后,还需要判断它是否能装得下

            4* “最优方案”:如果可以装得下,我们需要选择一 个价值之和最大的

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int w[1001],v[1001],n;
    int ans,t;
    void dfs(int x,int totw,int totv)
    {
        if(x>n)
        {
            if(totw>ans)
                ans=totw;
            return ;
        }
        else
        {
            dfs(x+1,totw,totv);
            if(totv+v[x]<=t)
            dfs(x+1,totw+w[x],totv+v[x]);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&t);
        for(int i=1;i<=n;i++)
        scanf("%d%d",&w[i],&v[i]);
        dfs(1,0,0);
        printf("%d",ans);
        return 0;
    }

    对于这个问题我们来拓展一下:

    1.如何记录拿了几件

    //多定义一个a[1001] b[1001]
    void dfs(int x,int totw,int totv)
    {
        if(x>n)
        {
            if(totw>ans)
                {
                    for(int i=1;i<=a[0];i++)
                    b[i]=a[i];
                    ans=totw;
                }
            return ;
        }
        else
        {
            dfs(x+1,totw,totv);
            if(totv+v[x]<=t)
            {
                ++a[0],a[a[0]]=x;
                dfs(x+1,totw+w[x],totv+v[x]);
                a[a[0]]=0,--a[0];
                //用a[0]来记录拿的个数 这个 也算一个技巧
            }
        }
    }

    2.完全背包(取无数件)

    void dfs(int x,int totw,int totv)
    {
        if(x>n)
        {
            if(totw>ans)
                ans=totw;
            return ;
        }
        else
        {
            dfs(x+1,totw,totv);
            while(totv+v[x]*m<=t)
            {
            dfs(x+1,totw+w[x]*m,totv+v[x]*m);
            m+=1;
            }
        }
    }

    3.多重背包:给了规定件数可以取,求最大价值

    void dfs(int x,int totw,int totv)
    {
        if(x>n)
        {
            if(totw>ans)
                ans=totw;
            return ;
        }
        else
        {
            dfs(x+1,totw,totv);
            for(int i=1;i<=member[x];i++)
            {
                if(totv+v[x]*i<t)
            dfs(x+1,totw+w[x]*i,totv+v[x]*i);
            m+=1;
            }
        }
    }

    当然,我们这里都是最最基本的搜索,欢迎补充优化

    * 分析哈密顿回路问题: 1 “划分游戏阶段”:走到了哪一个点

                2 “行动”:下一步走哪一个点

                3* “胜利”:把全部点都走遍了

                4* “约束”:在过程中有没有走到重复的点

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N = 25;
    
    int n, cnt;
    int a[N][N];
    int v[N], p[N];
    
    void print()
    {
        printf("Route #%d: ", ++cnt);
        for(int i=1; i<=p[0]; i++) printf("%d - ", p[i]);
        printf("1
    ");
    }
    
    void dfs(int x)
    {
        v[x] = 1, p[++p[0]] = x;//记录走的那几个点 
    
        if(p[0] == n)
        {
            if(a[p[0]][1]) print();//如果头和尾也相连,输出 
        }
        else for(int i=1; i<=n; i++) 
                if(a[x][i] && !v[i])//有路径并且没有走过->搜索 
                    dfs(i);
        
        v[x] = 0, p[p[0]] = 0, --p[0];//回溯 
    }
    
    int main()
    {
    //    freopen("hamilton.in", "r", stdin);
    //    freopen("hamilton.out", "w", stdout);
    
        scanf("%d", &n);
        for(int i=1; i<=n; i++) 
            for(int j=1; j<=n; j++) 
                scanf("%d", &a[i][j]);//用邻接矩阵来存储的哈密尔顿环 
    
        dfs(1);
    
        return 0;
    }

    *POJ 1979  红与黑:考虑 问题的各个步骤
      1 阶段/ 搜索顺序:一步一步的走
      2 状态:目前走到的位置
      3 行动:向上下左右各走一步
      4 胜利条件/ 结束条件:没有地方可以走了
      5 约束:不能走出房间/ 不能走到红色瓷砖上

    #include <cstdio>
    using namespace std;
    
    const int N = 25;
    const int dx[4] = {1, 0, -1, 0};
    const int dy[4] = {0, 1, 0, -1};
    
    int n, m, sx, sy, cnt;
    int a[N][N], vs[N][N];
    
    void dfs(int x, int y)
    {
        ++cnt, vs[x][y] = 1;//计数加标记 
        for(int d=0; d<4; d++)
        {
            int i=x+dx[d], j=y+dy[d];//上下左右四个方向 
            if(i<1 || j<1 || i>n || j>m || a[i][j]=='#' || vs[i][j]) continue;
            //不越界,不违法约束 
            dfs(i, j);
        }
    }
    //当然,这道题用bfs也可以做,但都差不多 
    int gc()
    {
        int c;
        while(c=getchar(), c<=' ');
        /*先输入数据,之后getchar()的返回值一个接一个赋给c,
         然后比较c是不是不等于' ',如果不等于回车键就会执行循环。
         如果等于就会结束*/ 
         //相当于用输入字符矩阵 
        return c;
    }
    
    void work()
    {
        for(int i=1; i<=n; i++) 
            for(int j=1; j<=m; j++)
        {
            a[i][j] = gc(), vs[i][j] = 0;
            if(a[i][j]=='@') sx = i, sy = j;//标记所给位置 
        }
        /*
        ch=getchar();等待从键盘上输入一个字符
        putchar(ch);输出此字符
        他们包含在头文件 #include<stdio.h>中.
        */ 
        cnt = 0;
        dfs(sx, sy);//从所给位置开始搜索 
        printf("%d
    ", cnt);
    }
    
    int main()
    {
        freopen("rnb.in", "r", stdin);
        freopen("rnb.out", "w", stdout);
        
        while(scanf("%d%d", &m, &n), n*m) work(); 
        //且m!=0||n!=0 ,当然它的数据是保证合法的 
        //用while循环输入,它的输入输出端是自动连在文件上的,
        //所以用键盘输入是不会有结果的 
        
        return 0;
    }

    DFS小总结:* 问题的各个步骤:
          1 阶段/ 搜索顺序    

          2 状态:全局状态记得回溯(有的不需要)   

          3 行动:枚举所有可能的行动
          4 胜利条件/ 结束条件
          5 约束

    下面我们来看BFS(前方高能预警)

    * BFS 搜索的思想是十分简单的: - 给定一个起始状态(将其记作 S0);

               - 记录从起始状态向外走一步能到达的状态(我们把它记作 S1);

               - 记录从 S1 走一步能到达的状态(将它们记作 S2);

               显然,这些 状态都能从起始状态走两步到达。

               * 最终,如果目标状态出现在 Sk 中,则显然它可以从起始状态 走k 步到达(所以最终答案是最小的 k)。

               * 我们称这种做法为逐步扩展

    八数码

    题目太经典就不显示了,而且这道题目曾经搞的我一晚上也没睡好,所以我今天一定要把它办掉

    八数码问题的状态即为棋盘上的八个数字

    从每一个空格开始上下左右各一种可能

    扩展出第一种状态,依次类推

    补充一点,bfs 是用队列来实现的

    但有一个很重要的问题就是 :如何判重,以及哪些是重复的。

    (好像这里有图的样子)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int main()
    {
        printf("1 2 3
    ");
        printf("5   6
    ");
        printf("7 8 9
    ");
        cout<<"---->"
        printf("         1 2 3
    ");
        printf("         5 6  
    ");
        printf("         7 8 9
    ");
        //那么把从这个空格开始向四个方向扩展,必定有一种情况是这样的
        printf("1 2 3
    ");
        printf("5   6
    ");
        printf("7 8 9
    ");
        //这样就回到原来的状态了,即为重复
        //而我们要克服的就是这些 
    } 

    接下来我们从程序入手

    本来应该是三步优化,一步比一步快,但我想,我写的既然是一个总结帖,我为什么不直接把最优的展示出来能?(好像很有道理的样子)


    其实是我懒

     下面我们看程序

    struct _state
    {
        int w[N][N];//当前这个棋盘的状态
        int x, y;//该空格的位置
        int step;//走了几步
        int pre, dirc;//父亲和对应的操作

    如何判重?

    如果我们开一个九维数组的话,哪怕是开bool型的,有以下几个麻烦之处

    1.写起来麻烦

    2.存起来费空间

    3.查找起来费时间

    4.看着别扭

    综上所述,我们聪明的前辈想到了一种很不错的存储办法,

    ------>用一个九位数来存

    这样只需要int就足够了

    像这样:

        int calc()
        {
            int ret = 0;
            for(int i=0; i<N; i++)
                for(int j=0; j<N; j++)
                    ret = ret * 10 + w[i][j];
            return ret;
        }

    那么程序实现就是

    int bfs(_state start, _state end)
    {
        head = tail = 1;
        start.step = 0;
        start.pre = -1;
        queue[1] = start;
        
        S.clear();
        S.insert(start.calc());
    
        for(; head<=tail; ++head)
        {
            _state cur = queue[head];
            int x = cur.x, y = cur.y;
    
            for(int i=0; i<4; i++)
            {
                int nx = x + dx[i], ny = y + dy[i];
                if(nx<0 || ny<0 || nx>=N || ny>=N) continue;
                            //枚举上下左右四个位置
                _state now = cur;//新的状态
                now.x = nx, now.y = ny;//新的空格位置
                swap(now.w[x][y], now.w[nx][ny]);//交换位置
                now.step++;//步数+1
                now.pre = head;//记录父亲
                now.dirc = i;//操作:空格移动的方向
    
                int nst = now.calc();
                if(S.find(nst) == S.end())//用set的一个判重,下面会讲:如果没有重复的数字
                    queue[++tail] = now,//加入队列
                    S.insert(nst);//并标记该状态
    
                if (equal(now,end)) return tail;
            }
        }
    
        return -1;
    }
                

    补充一点:set是个神马东东

     set集合容器实现了红黑树(Red-Black Tree)的平衡二叉检索树的的数据结构,在插入元素时,它会自动调整二叉树的排列,把该元素放到适当的位置,以确保每个子树根节点的键值大于左子树所有节点的键值,而小于右子树所有节点的键值;另外,还得确保根节点的左子树的高度与右子树的高度相等,这样,二叉树的高度最小,从而检索速度最快。要注意的是,它不会重复插入相同键值的元素,而采取忽略处理。

            平衡二叉检索树的检索使用中序遍历算法,检索效率高于vector、deque、和list的容器。另外,采用中序遍历算法可将键值由小到大遍历出来,所以,可以理解为平衡二叉检索树在插入元素时,就会自动将元素按键值从小到大的顺序排列。

            构造set集合的主要目的是为了快速检索,使用set前,需要在程序头文件中包含声明“#include<set>”。

    采用inset()方法把元素插入到集合中,插入规则在默认的比较规则下,是按元素值从小到大插入,如果自己指定了比较规则函数,则按自定义比较规则函数插入。使用前向迭代器对集合中序遍历,结果正好是元素排序后的结果。

    下面我先放一波程序

    #include <set>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N = 3;
    const int M = 1e6 + 5;
    
    const int dx[4] = {0, 0, 1, -1};
    const int dy[4] = {1, -1, 0, 0};//四个方向搜索的小技巧
    const char mov[4] = {'R', 'L', 'D', 'U'};
    
    struct _state
    {
        int w[N][N];
        int x, y;
        int step;
        int pre, dirc;
    
        void read()//读入矩阵并存储空格位置
        {
            for(int i=0; i<N; i++)
                for(int j=0; j<N; j++)
                {
                    scanf("%d", &w[i][j]);
                    if(!w[i][j]) x=i, y=j;
                }
        }
    
        int calc()//将矩阵改为一个九位数
        {
            int ret = 0;
            for(int i=0; i<N; i++)
                for(int j=0; j<N; j++)
                    ret = ret * 10 + w[i][j];
            return ret;
        }
    };
    
    int equal(_state x, _state y)//
    {
        for(int i=0; i<N; i++)
         for(int j=0; j<N; j++)
            if(x.w[i][j] != y.w[i][j]) return 0;
        return 1;
    }
    
    _state queue[M];
    set<int> S;
    
    int head, tail;
    
    int bfs(_state start, _state end)//开始状态,结束状态各有两个矩阵来表示
    {
        head = tail = 1;//队列为空
        start.step = 0;//初始步骤为零
        start.pre = -1;//初始状态没有父亲
        queue[1] = start;//初始状态入队
    
        S.clear();//清空set
        S.insert(start.calc());//插入这个矩阵所转化为的九位数
    
        for(; head<=tail; ++head)//如果队列不为空
        {
            _state cur = queue[head];//取队首
            int x = cur.x, y = cur.y;//复制空格位置
    
            for(int i=0; i<4; i++)
            {
                int nx = x + dx[i], ny = y + dy[i];
                if(nx<0 || ny<0 || nx>=N || ny>=N) continue;
    
                _state now = cur;
                now.x = nx, now.y = ny;
                swap(now.w[x][y], now.w[nx][ny]);//拓展空格位置
                now.step++;
                now.pre = head;//记录是第几步
                now.dirc = i;//从哪个方向拓展来的
    
                int nst = now.calc();
                if(S.find(nst) == S.end())//end()返回集合里最后一个元素的迭代器
                          //返回指向被查找元素的迭代器
        //find()可以查找一个元素在集合里的迭代器,如果该元素未出现过,则返回end() queue[
    ++tail] = now,//(注意是逗号)使该元素入队 S.insert(nst); if (equal(now,end)) return tail;//如果该矩阵已经和要求矩阵一样了 } } return -1; } int seq[N]; void print(int no, int x) { printf("Step #%d ", x); for(int i=0; i<N; i++) { for(int j=0; j<N; j++)
           printf("%d ", queue[no].w[i][j]);//输出当前步数下的矩阵 printf(" "); } } int main() { //freopen("eight.in", "r", stdin); // freopen("eight.out", "w", stdout); _state start, end; start.read(); end.read(); int ans = bfs(start, end); printf("%d ", queue[ans].step);

    s:
    for(int i=tail; i>=0; i=queue[i].pre) seq[++seq[0]] = i;//用s[0]来存储个数 for(int i=seq[0]; i>=1; i--) print(seq[i], seq[0]-i);//输出矩阵
    printf(
    "Moving Sequence: "); for(int i=seq[0]-1; i>=1; i--) printf("%c", mov[ queue[seq[i]].dirc ]);//输出该矩阵的操作 return 0; }

     注意,bfs是逐层扩展的,相当于给第一次一个编号,给第二层一个编号。。。。。。

    不要与dfs混淆

    下面我们看跳马问题

    跳马就是八个方向遍历,然后判重,如程序

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N = 303;
    const int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2};
    const int dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};
    
    struct _state
    {
        int x, y, step;
    };
    
    int t, n;
    int sx, sy, tx, ty;
    int vs[N][N];
    int head, tail;
    _state que[N * N];
    
    void work()
    {
        scanf("%d", &n);
        scanf("%d%d", &sx, &sy);
        scanf("%d%d", &tx, &ty);
    
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++) vs[i][j] = 0;//标记初始状态全部为未走过
    
        head = tail = 1;
        que[head].x = sx;
        que[head].y = sy;//入队
        que[head].step = 0;//这个步数还是逐层扩展的
        vs[sx][sy] = 1;
    
        for(; head <= tail; ++head)
        {
            int x = que[head].x;
            int y = que[head].y;
            int step = que[head].step;
    
            if(x == tx && y == ty)//到达目标,输出
            {
                printf("%d
    ", step);
                return;
            }
    
            for(int i=0; i<8; i++)
            {
                int nx = x + dx[i];
                int ny = y + dy[i];
                int nstep = step + 1;//大佬做深/广搜题喜欢先把下一步的状态写出来,然后再判断,再入队/搜索
    
                if(nx<0 || ny<0 || nx>=n || ny>=n) continue;
                if(vs[nx][ny]) continue;
    
                ++tail;
                que[tail].x = nx;
                que[tail].y = ny;
                que[tail].step = nstep;
                vs[nx][ny] = 1;//这里就是判重,使得它必定能找到解
            }
        }
    }
    
    int main()
    {
        //freopen("knight.in", "r", stdin);
        //freopen("knight.out", "w", stdout);
    
        scanf("%d", &t);
        for(int i=1; i<=t; i++) work();
    
        return 0;
    }

    接下来是倒水问题,

     倒水问题:有两个水壶,容积分别为 A,B。有三种操作: Fill(往一个水壶倒满水),Drop(倒出某个水壶的水),Pour(将 其中一个水壶的水倒入另一个水壶中,倒满即止)。

    - 问倒出 C 升水,最少的步数是多少。

    这个我们就分六步来进行讨论

    设水壶a,b;

    1.倒空a

    2.倒空b;

    3.倒满a;

    4.倒满b;

    5.把a倒向b(这里需要判断a,b水量)

    6.把b倒向a(这里需要判断a,b水量)

    具体操作如下

    
    

    #include <cstdio>
    #include <string>
    #include <algorithm>
    using namespace std;

    
    

    const int N = 103;

    
    

    const string operate[6] =
    {
    "FILL(1)", "FILL(2)", "DROP(1)",
    "DROP(2)", "POUR(1,2)", "POUR(2,1)"
    };

    
    

    struct _state//每一个水杯状态所含的消息
    {
    int x, y;
    int step;
    int pre, op;
    };

    
    

    int a, b, c;
    int vs[N][N];
    int head, tail;
    _state que[N * N];

    
    

    int seq[N];

    
    

    void print(int x)
    {
    printf("%d ", que[x].step);
    for(int i=x; i>=0; i=que[i].pre)
    seq[++seq[0]] = i;
    for(int i=seq[0]-1; i>=1; i--)
    printf("%s ", operate[que[seq[i]].op].c_str());//c_str()下文有讲,

    }

    //总结一下这种倒向输出转移状态的步骤

    /*

    1.新开一个数组来倒叙存储下标

    2.一个倒叙for,输出最后一步所在队列的状态,用刚才存储的下标来做

    3.前提:你在进行bfs扩展时,已经存储了它的父节点,已经操作方式

    */

    
    

    void add_state(int nx, int ny, int nstep, int pre, int op)
    {
    if(vs[nx][ny]) return;

    
    

    vs[nx][ny] = 1;//标记该状态已经走过

    
    

    ++tail;
    que[tail].x = nx;
    que[tail].y = ny;
    que[tail].step = nstep;
    que[tail].pre = pre;
    que[tail].op = op;

    //入队

    }

    
    

    void work()
    {
    scanf("%d%d%d", &a, &b, &c);

    
    

    for(int i=0; i<=a; i++)
    for(int j=0; j<=b; j++) vs[i][j] = 0;

    
    

    head = tail = 1;
    que[head].x = 0;
    que[head].y = 0;
    que[head].step = 0;
    que[head].pre = -1;
    vs[0][0] = 1;//初始状态入队

    
    

    for(; head <= tail; ++head)
    {
    int x = que[head].x;
    int y = que[head].y;
    int step = que[head].step;//取队首

    
    

    if(x == c || y == c)
    {
    print(head);//正解,输出
    return;
    }

    
    

    // Fill
    add_state(a, y, step+1, head, 0);
    add_state(x, b, step+1, head, 1);

    
    

    // Drop
    add_state(0, y, step+1, head, 2);
    add_state(x, 0, step+1, head, 3);

    
    

    // Pour
    int aug = min(x, b - y);
    add_state(x-aug, y+aug, step+1, head, 4);

    
    

    aug = min(y, a - x);
    add_state(x+aug, y-aug, step+1, head, 5);
    }

    
    

    puts("impossible");
    }

    
    

    int main()
    {
    freopen("cups.in", "r", stdin);
    freopen("cups.out", "w", stdout);

    
    

    work();

    
    

    return 0;
    }

     

    补充:

    函数声明:const char *c_str();

    c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同.

    这是为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成c中的字符串样式。

    注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针

    例:

    应该这样用:

    char c[20];

    string s="1234";

    strcpy(c,s.c_str());

    这样才不会出错,c_str()返回的是一个临时指针,不能对其进行操作

    c_str()返回的是一个分配给const char*的地址,其内容已设定为不可变更,如果再把此地址赋给一个可以变更内容的char*变量,就会产生冲突,在2010中是不被允许的。但是如果放入函数调用,或者直接输出,因为这些函数和输出都是把字符串指针作为 const char*引用的,所以不会有问题。

    c_str() 以const char* 类型返回 string 内含的字符串

    如果一个函数要求char*参数,可以使用c_str()方法:

    string s = "Hello World!";

    printf("%s", s.c_str()); //输出 "Hello World!"

    c_str在打开文件时的用处:

    当需要打开一个由用户自己输入文件名的文件时,可以这样写:ifstream in(st.c_str());。其中st是string类型,存放的即为用户输入的文件名。

    bfs小结

    * BFS 问题的各个步骤:

    1 阶段/ 搜索顺序:题目会明确地给出

    2 状态:与 DFS 类似,要保存所有的信息

    3 行动:与 DFS 类似,枚举所有可能的行动

    4 胜利条件/ 结束条件:题目明确地给出

    5 约束

    6* 判重:如何判断状态已经重复?- DFS 问题中,使用判重来减少搜索量:记忆化搜索

    7* 输出方案:通过记录父亲状态,一步一步回推。 - 为什么在 DFS 问题中,我们没有着重提出如何输出方案?

    好了,那么到现在为止,基础搜索题目我已经把我所学到的全部整理完了,

    如果你认为还有和这些题目同样经典的,请私信找我

    byebye!(^o^)/~

    我博客里有大量的从别的博客复制过来的代码,分析,以及理解,但我一律会在文章后面标记原博客大佬博客名,其中部分会加以连接。 绝无抄袭的意思,只是为了我在复习的时候找博客方便。 如有原作者对此有不满,请在博客留言,我一定会删除该博文。
  • 相关阅读:
    https://blog.csdn.net/yongchaocsdn/article/details/53355296
    P1526 [NOI2003]智破连环阵 [搜索+剪枝(二分图)]
    AT2165 Median Pyramid Hard [二分答案]
    翻煎饼 [迭代加深搜索+剪枝]
    P2962 [USACO09NOV]灯Lights [高斯消元+异或方程组 / 折半搜索]
    P5025 [SNOI2017]炸弹 [线段树优化建图 + Tarjan]
    Tarjan [割点, 缩点, 桥(待填坑)]
    线段树优化建图学习笔记
    P5468 [NOI2019]回家路线 [斜率优化dp]
    CF573E Bear and Bowling [平衡树+动态规划]
  • 原文地址:https://www.cnblogs.com/ZDHYXZ/p/6879682.html
Copyright © 2011-2022 走看看