zoukankan      html  css  js  c++  java
  • 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
    ", 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("
    ");
    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
    ", ++t);
    66         else printf("Game %d: Impossible
    ", ++t);
    67     }
    68 
    69     return 0;
    70 }
    他坐在湖边,望向天空,她坐在对岸,盯着湖面
  • 相关阅读:
    复数加法
    通过函数来实现复数相加
    声明一个类模板
    友元成员函数的简单应用
    将普通函数声明为友元函数
    引用静态数据成员
    对象的赋值
    对象的常引用
    有关对象指针的使用方法
    对象数组的使用方法
  • 原文地址:https://www.cnblogs.com/Inkblots/p/5290104.html
Copyright © 2011-2022 走看看