ACM/ICPC 之 DFS范例(ZOJ2412-ZOJ1008)

通过几道例题简单阐述一下DFS的相关题型

 

 

ZOJ2412-Farm Irrigation

  直观的DFS题型,稍加变化,记录好四个方向上的通路就能够做出来

  题目和接水管类似,问最少要灌溉几次,即求解最少有多少个连通子图。

  

 1 //和接水管游戏类似,将相应水管通路标记清晰即可
 2 //Time:0Ms    Memory:270K
 3 #include<iostream>
 4 #include<cstring>
 5 #include<cstdio>
 6 using namespace std;
 7 #define MAX 55
 8 #define OPPOSITE(x) ((x + 2) % 4)    //x的相反位置
 9 #define MAP(x,y) (map[x][y] - 'A')    //水管序列
10 int row, col;
11 char map[MAX][MAX];
12 bool v[MAX][MAX];
13 int mov[4][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };    //东南西北
14 bool face[4][11] = {    //face[i][j] : 在i方向上第j个水管是否有通路
15     { 0,1,0,1,0,1,1,0,1,1,1 },    //
16     { 0,0,1,1,1,0,0,1,1,1,1 },    //
17     { 1,0,1,0,0,1,1,1,1,0,1 },    //西
18     { 1,1,0,0,1,0,1,1,0,1,1 }    //
19 };
20 void dfs(int x,int y)
21 {
22     v[x][y] = true;
23     for (int i = 0; i < 4; i++)
24     {
25         int tx = x + mov[i][0];
26         int ty = y + mov[i][1];
27         if (face[i][MAP(x,y)] && tx >= 0 && tx < row && ty >= 0 && ty < col)    //该水管相应对接方向有通路
28         {
29             if (!v[tx][ty] && face[OPPOSITE(i)][MAP(tx,ty)])    //对接水管相应方向有通路
30                 dfs(tx, ty);
31         }
32     }
33 }
34 int main()
35 {
36     while (scanf("%d%d", &row, &col), row != -1 && col != -1)
37     {
38         memset(v, false, sizeof(v));
39         for (int i = 0; i < row; i++)
40             scanf("%s", map[i]);
41         int times = 0;
42         for (int i = 0; i < row; i++)
43             for (int j = 0; j < col; j++)
44             {
45                 if (!v[i][j]) {
46                     times++;
47                     dfs(i, j);
48                 }
49             }
50         printf("%d\n", times);
51     }
52     return 0;
53 }

 

 

ZOJ1008-Gnome Tetravex

  看起来不像个搜索题,初看可能会以为需要枚举之类的,但是题中方块的各状态需要记录,在匹配失败时需要回退,因此是一道DFS题型。

  大致的解题思路就是先枚举0行0列的方块,再依据此方块固定下一个方块,以此类推,出现不能匹配时回退。

  虽然规模最大只有5,但是总方块数25个在DFS中也不可小觑,如果用纯DFS来做,时间度最坏可以达到O((n^2)!),因此剪枝或其他优化是必须的(只要数据不够水),我在超时后看了很多博客的解题报告(想不到了= =),就该题数据而言最好的优化方法是去重,即将相同方块合在一起,以减少DFS的分支数,但是我总觉得这么做挺奇怪的。。。虽然在大数据下,此题去重很有效果(数字在0-9,因此方块重复概率 < 1/2500),但是此题拿小数据故意出多组重复方块,不得不让人怀疑其心不善…

  

  另外的剪枝方法,也有很多比较有效,但对此题没有太大帮助。

    例如:记录各方向上各数字的个数,在匹配时动态增删,如果已经固定的方块需要的对应数字的数量缺失,那么就可以直接回退,剪枝效果在少量随机数据情况下比较好。

    

 1 //去重就不会超时了,但这个优化实在是...让人感到很意外
 2 //Time:2100ms    Memory:272K
 3 #include<iostream>
 4 #include<cstring>
 5 #include<cstdio>
 6 using namespace std;
 7 
 8 #define MAX 28
 9 
10 int n, m;
11 int sq[MAX][4];    //上右下左
12 int board[MAX];    //棋盘上对应位置的square
13 int v[MAX];    //记录同类sq未被使用的数量
14 
15 bool dfs(int num)
16 {
17     if (num == n*n)
18         return true;
19     //找出可以充当第num个方块的i方块
20     for (int i = 0; i < m; i++)
21     {
22         if (!v[i]) continue;    //没有未固定方块
23         if (num % n && sq[i][3] != sq[board[num - 1]][1]) continue;    //非第一列+不匹配
24         if (num / n && sq[i][0] != sq[board[num - n]][2]) continue;    //非第一行+不匹配
25         
26         v[i]--;
27         board[num] = i;
28         if (dfs(num + 1))    return true;
29         else v[i] ++;
30     }
31     return false;
32 }
33 
34 int main()
35 {
36     int t = 0;
37     while (scanf("%d", &n), n)
38     {
39         if (t)    printf("\n");
40 
41         memset(v, 0, sizeof(v));
42         memset(board, 0, sizeof(board));
43         m = 0;
44         for (int i = 0; i < n*n; i++)
45         {
46             int u, r, d, l;
47             scanf("%d%d%d%d", &u, &r, &d, &l);
48             //查重
49             bool flag = false;
50             for (int j = 0; j < m; j++)
51             {
52                 if (u == sq[j][0] && r == sq[j][1] && d == sq[j][2] && l == sq[j][3])
53                 {
54                     v[j]++;
55                     flag = true;
56                     break;
57                 }
58             }
59             if (!flag) {
60                 sq[m][0] = u; sq[m][1] = r; sq[m][2] = d; sq[m][3] = l;
61                 v[m++] ++;
62             }
63         }
64 
65         if (dfs(0))    printf("Game %d: Possible\n", ++t);
66         else printf("Game %d: Impossible\n", ++t);
67     }
68 
69     return 0;
70 }

 

    原文作者:算法小白
    原文地址: https://www.cnblogs.com/Inkblots/p/5290104.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞