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 }