ACM/ICPC 之 拓扑排序+DFS(POJ1128(ZOJ1083)-POJ1270)

两道经典的同类型拓扑排序+DFS问题,第二题较第一题简单,其中的难点在于字典序输出+建立单向无环图,另外理解题意是最难的难点,没有之一…

 

 

 

POJ1128(ZOJ1083)-Frame Stacking

 

  题意:每个图片由同一字母组成的边框表示,每个图片的字母都不同;

     在一个最多30*30的区域放置这些图片,问底层向顶层叠加的图片次序,多选时按字典序输出

     注:每个图片的四边都会有字符显示,其中顶点显示两边。

  题解:题意的理解是难点,题目对图片的范围确定说得有点含糊不清,博主一开始就被出现的五张图片的样例迷惑,理解重心放错了。题目最需要理解的是下方的三句话。

     第一句和数据范围就确定了图片尺寸

     第二句话提示读者应该考虑记录对角线上的两个顶点,以此记录该图片的位置和尺寸

     第三句话确定了图片的数量及可以标明该图片的key值(字母)

     最后要注意Input中有多组样例,而Ouput中的多组次序需要按照字典序输出

 

     理清题意后:接下来的工作就是先用两个顶点确立图片的位置和尺寸

           接着利用各图片的位置和尺寸确定覆盖关系建立单向无环图

           最后利用DFS的回溯完成字典序的拓扑排序即可。

 

  1 //叠图片-拓扑排序+DFS
  2 //每个图片由同一字母组成的边框表示,每个图片的字母都不同
  3 //在一个最多30*30的区域放置这些图片,问底层向顶层叠加的图片次序,多选时按字典序输出
  4 //注:每个图片的四边都至少会有一个字符显示
  5 //Time:0Ms    Memory:180K
  6 #include<iostream>
  7 #include<cstring>
  8 #include<cstdio>
  9 #include<vector>
 10 #include<algorithm>
 11 using namespace std;
 12 
 13 #define MAXN 31    //地图长宽
 14 #define MAXL 26    //字母
 15 
 16 struct Coordinate{
 17     int x, y;
 18 }lt[MAXL], rb[MAXL];    //left_top - right_bottom
 19 
 20 struct Letter {
 21     vector<int> covered;
 22     int in;            //in_degree
 23     bool exist;
 24 }let[MAXL];
 25 
 26 int row, col;
 27 int total;    //字母个数
 28 char ans[MAXL+1];
 29 char board[MAXN][MAXN];
 30 
 31 void dfs(int len)
 32 {
 33     if (len == total)
 34     {
 35         ans[len] = '\0';
 36         printf("%s\n", ans);
 37         return;
 38     }
 39 
 40     for (int i = 0; i < MAXL; i++)
 41     {
 42         if (!let[i].exist) continue;
 43         if (let[i].in == 0)
 44         {
 45             ans[len] = i + 'A';
 46             let[i].in--;
 47             for (int j = 0; j < let[i].covered.size(); j++)
 48                 let[let[i].covered[j]].in--;
 49             dfs(len + 1);
 50             for (int j = 0; j < let[i].covered.size(); j++)
 51                 let[let[i].covered[j]].in++;
 52             let[i].in++;
 53         }
 54     }
 55 
 56 }
 57 
 58 int main()
 59 {
 60     while (scanf("%d%d", &row, &col) != EOF)
 61     {
 62         total = 0;
 63         memset(let, 0, sizeof(let));
 64         memset(lt, 0x3f, sizeof(lt));
 65         memset(rb, -1, sizeof(rb));
 66         //Input and Init
 67         for (int i = 0; i < row; i++)
 68         {
 69             scanf("%s", board[i]);
 70             for (int j = 0; j < col; j++)
 71             {
 72                 if (board[i][j] == '.') continue;
 73                 int t = board[i][j] - 'A';
 74                 if (!let[t].exist)
 75                     let[t].exist = ++total;
 76                 lt[t].x = min(lt[t].x, i);
 77                 lt[t].y = min(lt[t].y, j);
 78                 rb[t].x = max(rb[t].x, i);
 79                 rb[t].y = max(rb[t].y, j);
 80             }
 81         }
 82 
 83         //get_Map
 84         for (int k = 0; k < MAXL; k++)
 85         {
 86             if (!let[k].exist) continue;
 87             for (int i = lt[k].x; i <= rb[k].x; i++)
 88                 for (int j = lt[k].y; j <= rb[k].y; j++)
 89                 {
 90                     if (i > lt[k].x && i < rb[k].x && j == lt[k].y + 1)
 91                         j = rb[k].y;
 92                     int cur = board[i][j] - 'A';
 93                     if (cur == k) continue;
 94                     let[k].covered.push_back(cur);
 95                     let[cur].in++;
 96                 }
 97         }
 98 
 99         //Topology and Output
100         dfs(0);
101     }
102     
103     return 0;
104 }

 

 

 

POJ1270-Following Orders

  本题相当于POJ1128的同类改编题型,相比1128简单。

 

  题意:给定第一行的小写字母,第二行每两个字母描述其约束关系,例如x y 即 x<y,求可以得到的所有有序序列。

  题解:这个题目的Input也是让人很醉啊,为了循环接受一行字符,需要使用读取到’\n’才停止的函数。

     可以实现的类似的函数有gets,fgets,sscanf;getchar(循环接受单字符);string的getline和cin的getline及get等等

     为了方便书写和之后的字符处理,折中选取了cin的getline函数

         cin.getline(str,MAX,’\n’):读入最多MAX大的字符串存入str,直到’\n’停止(可以不需要第三个参数)

     之后的处理和POJ1128的题解一致,依旧是构图+拓扑排序+DFS,不再累述。

 

 1 //有序序列-拓扑排序+DFS
 2 //一个难点在于输入,如果使用C语言的gets或者fgets总觉得不好处理
 3 //    于是利用了cin的getline函数,会将'\n'转换成'\0',利于处理
 4 //另一个难点在于字典序输出,需要利用到DFS的回溯
 5 //其余的操作与拓扑排序相同
 6 //Time:0Ms    Memory:208K
 7 #include<iostream>
 8 #include<cstring>
 9 #include<cstdio>
10 #include<vector>
11 #include<algorithm>
12 using namespace std;
13 
14 #define MAX 50
15 
16 struct Letter {
17     vector<int> bigger;
18     int in;        //in_degree
19     bool exist;
20 }let[26];
21 
22 int total;
23 char ans[MAX];
24 
25 void dfs(int len)
26 {
27     if (len == total)
28     {
29         ans[len] = '\0';
30         printf("%s\n", ans);
31         return;
32     }
33 
34     for (int i = 0; i < 26; i++)
35     {
36         if (!let[i].exist || let[i].in) continue;
37         let[i].in--;
38         for (unsigned j = 0; j < let[i].bigger.size(); j++)
39             let[let[i].bigger[j]].in--;
40         ans[len] = i + 'a';
41         dfs(len + 1);
42         for (unsigned j = 0; j < let[i].bigger.size(); j++)
43             let[let[i].bigger[j]].in++;
44         let[i].in++;
45     }
46 }
47 
48 int main()
49 {
50     char str[MAX];
51     while (cin.getline(str, MAX, '\n'))
52     {
53         total = 0;
54         memset(let, 0, sizeof(let));
55         int len = strlen(str);
56         for (int i = 0; i < len; i += 2)
57             if(!let[str[i] - 'a'].exist)
58                 let[str[i] - 'a'].exist = ++total;
59 
60         cin.getline(str, MAX, '\n');
61         len = strlen(str);
62         for (int i = 0; i < len; i += 4)
63         {
64             int small = str[i] - 'a';
65             int big = str[i + 2] - 'a';
66             let[small].bigger.push_back(big);
67             let[big].in++;
68         }
69 
70         dfs(0);
71         printf("\n");
72     }
73     
74     return 0;
75 }

 

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