|
| 1 | +## [[POJ](http://poj.org/)] [[INDEX](https://github.com/lyy289065406/POJ-Solving-Reports)] [1094] [[Sorting It All Out](http://poj.org/problem?id=1094)] |
| 2 | + |
| 3 | +> [Time: 1000MS] [Memory: 10000K] [难度: 初级] [分类: 拓扑排序] |
| 4 | +
|
| 5 | +------ |
| 6 | + |
| 7 | +## 问题描述 |
| 8 | + |
| 9 | +无。 |
| 10 | + |
| 11 | + |
| 12 | +## 解题思路 |
| 13 | + |
| 14 | +**提示:** 拓扑排序 |
| 15 | + |
| 16 | +这道题有隐含这一信息,每输入一对关系,如果判定有结果,则可以忽略后面输入数据,即使后面输入数据能改变结果,也不用管。所以应该每输入一个关系就去更新当前的图,然后进行一趟拓扑排序。一旦产生结果,再对后面的数据处理下,就可以输出结果。 |
| 17 | + |
| 18 | +------ |
| 19 | + |
| 20 | +下面罗列所有可能的情况(类似于**状态机**): |
| 21 | + |
| 22 | + |
| 23 | +一、当输入的字母全部都在前n个大写字母范围内时: |
| 24 | + |
| 25 | +(1)最终的图 可以排序: |
| 26 | + 在输入结束前如果能得到最终的图(就是用这n个字母作为顶点,一个都不能少); |
| 27 | + 而且最终得到的图 无环; |
| 28 | + 只有唯一一个 无前驱(即入度为0)的结点,但允许其子图有多个无前驱的结点。 |
| 29 | + 在这步输出排序后,不再对后续输入进行操作 |
| 30 | + |
| 31 | +(2)输出矛盾 |
| 32 | + 在输入结束前如果最终图的子图有环 |
| 33 | + 在这步输出矛盾后,不再对后续输入进行操作 |
| 34 | + |
| 35 | +(3)输出无法确认排序 |
| 36 | + 这种情况必须全部关系输入后才能确定,其中又有2种可能 |
| 37 | + ① 最终图的字母一个不缺,但是有多个 无前驱结点 |
| 38 | + ② 输入结束了,但最终的图仍然字母不全,与 无前驱结点 的多少无关 |
| 39 | + |
| 40 | +二、当输入的字母含有 非前n个大写字母 的字母时(超出界限): |
| 41 | + |
| 42 | +(1)输出矛盾 |
| 43 | + 输入过程中检查输入的字母(结点),若 前n个大写字母 全部出现,则在最后一个大写字母出现的那一步 输出矛盾 |
| 44 | + |
| 45 | +(2)输出无法确认排序 |
| 46 | + 最后一步输入后,前n个大写字母 仍然未全部出现,则输出 无法确认排序 |
| 47 | + |
| 48 | +------ |
| 49 | + |
| 50 | +**注意:** |
| 51 | + |
| 52 | +在使用“无前驱结点”算法时必须要注意,在“矛盾优先”的规律下,必须考虑一种特殊情况,就是多个无前驱结点与环共存时的情况,即输入过程中子图都是有 多个无前驱结点,最后一步输入后出现了环,根据算法的特征,很容易输出“不能确认排序”,这是错的,必须适当修改算法,输出“矛盾”。 |
| 53 | + |
| 54 | +例如: |
| 55 | + |
| 56 | +``` |
| 57 | +6 6 |
| 58 | +A<F |
| 59 | +B<D |
| 60 | +C<E |
| 61 | +F<D |
| 62 | +D<E |
| 63 | +E<F |
| 64 | +``` |
| 65 | + |
| 66 | +输出矛盾 |
| 67 | + |
| 68 | + |
| 69 | +## AC 源码 |
| 70 | + |
| 71 | + |
| 72 | +```c |
| 73 | +//Memory Time |
| 74 | +//276K 0MS |
| 75 | + |
| 76 | +#include<iostream> |
| 77 | +using namespace std; |
| 78 | + |
| 79 | +int n,m; //n结点下限,m关系对 |
| 80 | +char top_out[26]; //排序输出列表 |
| 81 | +int po=0; //输出列表的指针 |
| 82 | + |
| 83 | +typedef class degree |
| 84 | +{ |
| 85 | +public: |
| 86 | + int in; //入度 |
| 87 | + char to[26]; //记录指向的所有顶点,以便删除出度的操作 |
| 88 | + int pt; //数组to的指针 |
| 89 | +}; |
| 90 | + |
| 91 | +int top_sort(degree alph[],bool mark[],int num) |
| 92 | +{ |
| 93 | + /*假设图G的当前子图为F*/ |
| 94 | + |
| 95 | + memset(top_out,'\0',sizeof(top_out)); |
| 96 | + po=0; |
| 97 | + |
| 98 | + int del_n=0; |
| 99 | + int zero=0; //记录图F中入度为0的结点个数 |
| 100 | + for(int i='A';i<'A'+n;i++) |
| 101 | + if(mark[i] && !alph[i].in) |
| 102 | + zero++; |
| 103 | + |
| 104 | + bool flag=false; |
| 105 | + while(zero>0) |
| 106 | + { |
| 107 | + if(zero>1) //图F的无前驱结点的个数不唯一,排序无法确定 |
| 108 | + flag=true; //考虑到"矛盾"的优先性,避免在多个0入度结点情况下,最后一步输入刚好出现环(此时为矛盾) |
| 109 | + //所以这里先不返回值,而是先标记,执行拓扑,根据情况决定返回值 |
| 110 | + |
| 111 | + for(int k='A';k<='A'+n;k++) //寻找图F的唯一的前驱结点 |
| 112 | + if(mark[k] && !alph[k].in) |
| 113 | + { |
| 114 | + mark[k]=false; //删除图F的唯一无前驱结点k |
| 115 | + del_n++; //记录删除的结点数 |
| 116 | + top_out[po++]=k; //k记录到排序输出列表 |
| 117 | + for(int i=0;i<alph[k].pt;i++) //删除结点k的所有出度边 |
| 118 | + alph[ alph[k].to[i] ].in--; |
| 119 | + break; |
| 120 | + } |
| 121 | + |
| 122 | + zero=0; |
| 123 | + for(int j='A';j<='A'+n;j++) |
| 124 | + if(mark[j] && !alph[j].in) |
| 125 | + zero++; |
| 126 | + } |
| 127 | + |
| 128 | + if(flag && del_n==num) |
| 129 | + return 3; |
| 130 | + if(del_n<num) //说明图F存在有向环,矛盾,与0入度结点的多少无关。因为矛盾优先 |
| 131 | + return 2; |
| 132 | + if(!flag && del_n==num && del_n<n) //图F能排序,但不能确定图G是否能排序,还需继续输入观察 |
| 133 | + return 3; |
| 134 | + if(!flag && del_n==n) //图G能排序 |
| 135 | + return 1; |
| 136 | +} |
| 137 | + |
| 138 | +int main(void) |
| 139 | +{ |
| 140 | + int num; //标记前n个字母出现个数,用于最终检查是否前n个字母均已被读入 |
| 141 | + //*_t[]是用于备份的额外数组 |
| 142 | + bool mark['Z'+1],mark_t['Z'+1]; //标记当前图G所使用的字母(结点) |
| 143 | + degree alph['Z'+1],alph_t['Z'+1]; |
| 144 | + |
| 145 | + while(true) |
| 146 | + { |
| 147 | + /*Input*/ |
| 148 | + |
| 149 | + cin>>n>>m; |
| 150 | + |
| 151 | + if(!n||!m) |
| 152 | + break; |
| 153 | + |
| 154 | + /*Initial*/ |
| 155 | + |
| 156 | + memset(mark,false,sizeof(mark)); |
| 157 | + memset(mark_t,false,sizeof(mark_t)); |
| 158 | + num=0; |
| 159 | + |
| 160 | + for(int k='A';k<'A'+n;k++) |
| 161 | + { |
| 162 | + alph[k].in=alph_t[k].in=0; |
| 163 | + alph[k].pt=alph_t[k].pt=0; |
| 164 | + memset(alph[k].to,'\0',sizeof(alph[k].to)); |
| 165 | + memset(alph_t[k].to,'\0',sizeof(alph_t[k].to)); |
| 166 | + } |
| 167 | + |
| 168 | + /*Structure Maps*/ |
| 169 | + |
| 170 | + char x,symbol,y; //临时变量 |
| 171 | + bool flag=false; |
| 172 | + bool sign=false; |
| 173 | + int value; //记录拓扑返回的值 |
| 174 | + int step; //记录当前情况发生的步骤 |
| 175 | + for(int pair=1;pair<=m;pair++) |
| 176 | + { |
| 177 | + cin>>x>>symbol>>y; |
| 178 | + |
| 179 | + if(x>='A'+n || y>='A'+n) //当输入的结点不在前n个字母范围内时 |
| 180 | + sign=true; //不再进行拓扑,单纯检查后续输入是否把前n个字母都输入了 |
| 181 | + //为了区分非前n个字母的字母的输入时间,是在确认了排序或矛盾之前还是之后 |
| 182 | + //在确认 排序或矛盾之前:flag=false,sign=true |
| 183 | + //在确认 排序或矛盾之后:flag=true,sign=true |
| 184 | + |
| 185 | + if(!mark[x] && x<'A'+n) |
| 186 | + num++; |
| 187 | + if(!mark[y] && y<'A'+n) |
| 188 | + num++; |
| 189 | + |
| 190 | + if(!flag && !sign) |
| 191 | + { |
| 192 | + value=0; |
| 193 | + |
| 194 | + mark[x]=mark[y]=true; //顶点标记 |
| 195 | + mark_t[x]=mark_t[y]=true; |
| 196 | + |
| 197 | + alph[y].in++; //入度标记 |
| 198 | + alph_t[y].in++; |
| 199 | + |
| 200 | + alph[x].to[ alph[x].pt++ ]=y; //指向标记 & 指针移动 |
| 201 | + alph_t[x].to[ alph_t[x].pt++ ]=y; |
| 202 | + |
| 203 | + /*Top-Sort & Sign*/ |
| 204 | + |
| 205 | + value=top_sort(alph_t,mark_t,num); //每次输入后图都被更新,要重新拓扑 |
| 206 | + if(value==1) //排序确认 |
| 207 | + { |
| 208 | + step=pair; //记录确认排序的位置 |
| 209 | + flag=true; //不再对后续输入处理 |
| 210 | + } |
| 211 | + else if(value==2) //矛盾 |
| 212 | + { |
| 213 | + step=pair; //记录矛盾发生的位置 |
| 214 | + flag=true; //不再对后续输入处理 |
| 215 | + } |
| 216 | + else if(value==3 && pair<m) //排序(暂时)无法确认,需继续处理后续输入 |
| 217 | + for(int k='A';k<'A'+n;k++) //数据还原 |
| 218 | + { |
| 219 | + mark_t[k]=mark[k]; |
| 220 | + alph_t[k].in=alph[k].in; |
| 221 | + } |
| 222 | + |
| 223 | + if(pair==m && value==0) |
| 224 | + value=3; |
| 225 | + } |
| 226 | + |
| 227 | + if(sign && !flag && num==n) //在确认 排序或矛盾之前,当存在有非前n个字母的结点时的"矛盾" |
| 228 | + { |
| 229 | + step=pair; |
| 230 | + value=2; |
| 231 | + } |
| 232 | + else if(sign && !flag && pair==m && num<n) //在确认 排序或矛盾之前,当存在有非前n个字母的结点时的"无法确认排序" |
| 233 | + value=3; |
| 234 | + } |
| 235 | + |
| 236 | + if(value==1) |
| 237 | + { |
| 238 | + cout<<"Sorted sequence determined after "<<step<<" relations: "; |
| 239 | + for(int i=0;i<po;i++) |
| 240 | + cout<<top_out[i]; |
| 241 | + cout<<'.'<<endl; |
| 242 | + } |
| 243 | + else if(value==2) |
| 244 | + cout<<"Inconsistency found after "<<step<<" relations."<<endl; |
| 245 | + else if(value==3) |
| 246 | + cout<<"Sorted sequence cannot be determined."<<endl; |
| 247 | + } |
| 248 | + return 0; |
| 249 | +} |
| 250 | +``` |
| 251 | +
|
| 252 | +------ |
| 253 | +
|
| 254 | +## 版权声明 |
| 255 | +
|
| 256 | + [-EXP%202016-blue.svg)](http://exp-blog.com) [](https://www.gnu.org/licenses/gpl-3.0) |
| 257 | + |
| 258 | +
|
| 259 | +- Site: [http://exp-blog.com](http://exp-blog.com) |
| 260 | +- Mail: <a href="mailto:289065406@qq.com?subject=[EXP's Github]%20Your%20Question%20(请写下您的疑问)&body=What%20can%20I%20help%20you?%20(需要我提供什么帮助吗?)">289065406@qq.com</a> |
| 261 | +
|
| 262 | +
|
| 263 | +------ |
0 commit comments