专题讲解--搜索例题(八皇后、骑士问题、母亲的牛奶)

 

1、八皇后问题

一个如下的  6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。


上面的布局可以用序列 2 4 6 1 3 5 来描述,第 i个数字表示在第 i 行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 6

列号 2 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 个解。最后一行是解的总个数。

输入格式
一行一个正整数 n,表示棋盘是 n×n大小的。

输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数

拿到这道题的第一个想法就是dfs。因为我们要保证每个皇后不在同一个对角线,不在一行,不在一列。所以我们每次把第k个皇后放在第k行,即保证每个皇后都不在同一行。接下来我们要判断每个皇后是否在一列或者对角线即可。我们设一个queen数组表示每个皇后所放位置所在列prey == ny || prey-prex == ny-nx || prey + prex == ny + nx。如果以上条件都不成立,那么皇后k的放置就是合理的。把当前合理的位置记录下来,以便下次遍历。

代码如下(由于代码比较卡,所以加了很多register又开了O2才过):

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cmath>
 5 #include <cstring>
 6 using namespace std;
 7 int n;
 8 bool vis[10];
 9 int a[10];
10 int queen[100];
11 bool flag;
12 int ans=0;
13 inline int read()
14 {
15     int x = 1,a = 0;
16     char ch = getchar();
17     while(ch < '0' || ch > '9'){
18         if(ch == '-')x = -1;
19         ch = getchar();
20     }
21     while(ch <= '9'&&ch >= '0'){
22         a = a * 10 + ch - '0';
23         ch = getchar();
24     }
25     return x*a;
26 }
27 inline void dfs(int k)
28 {
29     if (k>n){
30         ans++;
31         if (ans<=3){
32             for (register int i = 1;i <= n;i++){
33                 printf ("%d ",queen[i]);
34             }
35             printf ("\n");
36         }
37     }
38     for (register int i = 1;i <= n;i++){
39     
40         int x = k,y = i;
41         flag=true;
42         for (register int j = 1;j < k&&flag;j++){
43             int prex = j,prey = queen[j];
44             if (y==prey||prey+prex==y+x||prex-prey==x-y)
45                 flag = false;
46             
47         }
48         if (flag){
49             queen[k] = y;
50             dfs(k+1);
51         }
52     }
53 }
54 int main()
55 {
56     n=read();
57     dfs(1);
58     printf ("%d\n",ans);
59     return 0;
60 }

 

题意翻译

输入8*8的国际象棋棋盘上的2个格子(列:a~h,行:1~8),求马至少多少步从起点(键盘输入的第一个位置)跳到终点(键盘输入的第二个位置)。

一个双向bfs的题解,好像还可以过八数码难题的说

  1. 题意:

    给定起始状态和结束状态,以及状态转换的规则,求最少的状态转换次数

  2. 解决:

    一般情况下,BFS 第一次遇到末状态时的深度即为“最少次数”,所以使用 BFS 求解

  3. 优化:

    一般的 BFS 通常是从某一状态开始搜索,某节点第一次达到结束状态时停止,属于“单向”搜索(单调向某一方向拓展)

    但在本题中给出了固定的起始状态和结束状态,这时可以使用 “ 双向BFS ” 进行优化,顾名思义——即从两个状态开始搜索,这时当两个搜索树第一次出现节点重合就得到了 “ 最少次数 ” 。

  4. 解释:

    ① 搜索的停止:从起始状态和结束状态拓展出来的搜索树第一次出现节点重合时,起始点和结束点之间就有一条路径相连接,即求得解。

    ② 起到优化作用的原因:如果把 BFS 搜索树都看成一棵二叉树,那么高度为h的二叉树至多有2^h-1个节点

代码实现:

  通常使用两个队列分别储存从两个状态开始的搜索状态,用vis数组判断是否重合,用dis数组储存每个节点的深度,遇到解的时候直接调用dis数组以输出。

  而搜索方式一般是:每次都选择节点个数少的那个队列拓展

  方位数组,表示骑士能走的八个方向。

  用两个数组vis1和vis2来记录分别从初位置和末位置所走到的点为1和2,判重的时候,只需要判断是否从初位置走的点碰到2或者从末位置走的点碰到了1。

  终于弄懂啦,代码如下!

 1 #include<queue>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=10;
 6 char a[N],b[N];
 7 int dx[N]={-1,-2,-2,-1,1,2,2,1};
 8 int dy[N]={-2,-1,1,2,2,1,-1,-2};
 9 struct node{
10     int x,y,t;
11 }st,ed,tmp;
12 int dis[N][N],vis[N][N];
13 int bfs()
14 {
15     if(st.x==ed.x&&st.y==ed.y) return 0;
16     queue<node> q1;
17     queue<node> q2;
18     vis[st.x][st.y]=1;
19     vis[ed.x][ed.y]=2;
20     q1.push(st);
21     q2.push(ed);
22     int fx,fy,xx,yy;
23     while(true){
24         if(q1.size()<q2.size()){
25             fx=q1.front().x;
26             fy=q1.front().y;
27             for(int i=0;i<8;i++){
28                 xx=fx+dx[i];yy=fy+dy[i];
29                 if(xx<1||xx>8||yy<1||yy>8) continue;
30                 if(vis[xx][yy]==0){
31                     tmp.t=q1.front().t+1;
32                     tmp.x=xx;tmp.y=yy;
33                     q1.push(tmp);
34                     vis[xx][yy]=1;
35                     dis[xx][yy]=tmp.t;
36                 }
37                 else if(vis[xx][yy]==2)
38                 return dis[xx][yy]+q1.front().t+1;
39             }q1.pop();
40         }
41         else{
42             fx=q2.front().x;
43             fy=q2.front().y;
44             for(int i=0;i<8;i++){
45                 xx=fx+dx[i];yy=fy+dy[i];
46                 if(xx<1||xx>8||yy<1||yy>8) continue;
47                 if(vis[xx][yy]==0){
48                     tmp.t=q2.front().t+1;
49                     tmp.x=xx;tmp.y=yy;
50                     q2.push(tmp);
51                     vis[xx][yy]=2;
52                     dis[xx][yy]=tmp.t;
53                 }
54                 else if(vis[xx][yy]==1)
55                 return dis[xx][yy]+q2.front().t+1;
56             }q2.pop();
57         }
58     }
59 }
60 int main(void)
61 {
62     while(scanf("%s%s",a,b)!=EOF){
63         st.x=a[0]-'a'+1;st.y=a[1]-'0';
64         ed.x=b[0]-'a'+1;ed.y=b[1]-'0';
65         st.t=ed.t=0;
66         memset(vis,0,sizeof(vis));
67         memset(dis,0,sizeof(dis));
68         printf("To get from %c%c to %c%c takes %d knight moves.\n",a[0],a[1],b[0],b[1],bfs());
69     }
70     return 0;
71 }

3.母亲的牛奶

题目描述

农民约翰有三个容量分别是 a,b,c 升的桶。

最初,a,b 桶都是空的,而 c 桶是装满牛奶的。有时,农民把牛奶从一个桶倒到另一个桶中,直到被灌桶装满或原桶空了。

当然每一次灌注都是完全的。由于节约,牛奶不会有丢失。

写一个程序去帮助农民找出当 a桶是空的时候,c桶中牛奶所剩量的所有可能性。

输入格式

单独的一行包括三个整数 a,b,c

输出格式

只有一行,升序地列出当 a 桶是空的时候,c 桶牛奶所剩量的所有可能性。

一共可以分六种情况:但是每一种情况有一个前提就是得到水的桶并没有装满

就拿其中的一种情况来说 a给b a剩余的水为va-(min(b,va+vb)-vb)如果b可容纳的比a原本的多,a就为空,反之,a剩水。va+vbmin(b,va+vb)对b来说如果b所能容纳的比a少,b就满了,反之b剩余vc,c中的水不变

对于其他五种情况a给c,b给a,b给c,c给a,c给b都是一样的

代码如下:

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 int a,b,c;
 7 bool vis[25][25][25];
 8 int k=0;int ans[100];
 9 void dfs(int va,int vb,int vc)
10 {
11     if (vis[va][vb][vc])return;
12     if (va==0) 
13     {
14         k++;
15         ans[k]=vc;
16     }
17     vis[va][vb][vc]=1;
18      if(vc){
19         if(va<a)
20         dfs(min(a,va+vc),vb,vc-(min(a,va+vc)-va));
21         if(vb<b)
22         dfs(va,min(vb+vc,b),vc-(min(b,vb+vc)-vb));
23     }
24     if(vb){
25         if(va<a)
26         dfs(min(a,va+vb),vb-(min(a,va+vb)-va),vc);
27         if(vc<c)
28         dfs(va,vb-(min(c,vc+vb)-vc),min(c,vc+vb));
29     }
30     if(va){
31         if(vb<b)
32         dfs(va-(min(b,va+vb)-vb),min(b,va+vb),vc);
33         if(vc<c)
34         dfs(va-(min(c,va+vc)-vc),vb,min(c,vc+va));
35     }
36     return;
37 }
38 int main()
39 {
40     scanf ("%d%d%d",&a,&b,&c);
41     dfs(0,0,c);    
42     sort(ans+1,ans+k+1);
43     for(int i=1;i<=k;i++)printf("%d ",ans[i]);
44     return 0;
45 }

 

posted @ 2020-01-15 20:51  小又又  阅读(466)  评论(0编辑  收藏  举报