ACM/ICPC 之 拓扑排序范例(POJ1094-POJ2585)

时间:2023-03-08 17:27:59

两道拓扑排序问题的范例,用拓扑排序解决的实质是一个单向关系问题


POJ1094(ZOJ1060)-Sortng It All Out    

  题意简单,但需要考虑的地方很多,因此很容易将code写繁琐了,会给力求code精简的强迫症患者一个警醒- -

  题意:给出m组逻辑关系式,求n个字母间的排序,分排序成功-排序矛盾-不能确定三种情况输出相应语句

  题解:拓扑排序,访问入度为0的结点,邻接点入度-1,然后继续访问入度为0的结点...直到访问完成为止

     需要注意的地方在于三种情况根据最早确定的情况来输出,例如先排序完成,但后面的关系式表明排序矛盾,此时按照先排序完成得到的序列输出。

    

 //一道考查拓扑排序的经典例题
//POJ1094-ZOJ1060
//Time:0Ms Memory:188K
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std; #define MAX 27 struct Letter {
int in; //入度
int v;
vector<int> p;
}letter[MAX]; int n, m;
char seq[MAX]; int topSort(int total)
{
memset(seq, , sizeof(seq));
int tmp[MAX]; //临时入度数组
for (int i = ; i < n; i++)
tmp[i] = letter[i].in; bool correct = true; //是否可行
for (int i = ; i < total;i++)
{
int cur; //当前字母位置
int cnt = ; //入度为0的个数
for (int i = ; i < n; i++)
if (tmp[i] == )
{
cur = i;
cnt++;
}
if (cnt == ) return -;
if (cnt > ) correct = false; //多选时-不可行但须判断是否矛盾
for (int i = ; i < letter[cur].p.size(); i++)
tmp[letter[cur].p[i]]--;
seq[i] = cur + 'A';
tmp[cur]--;
}
return correct;
} int main()
{
//freopen("in.txt", "r", stdin);
while (scanf("%d%d", &n, &m), n && m)
{
int total = ; //输入字母总个数
bool flag = false; //得到结论
memset(letter, , sizeof(letter));
for (int i = ; i <= m; i++)
{
char formula[];
scanf("%s", formula);
if (flag) continue; //已得出结论
int small = formula[] - 'A';
int big = formula[] - 'A';
total += !letter[small].v + !letter[big].v;
letter[small].v = letter[big].v = ;
letter[small].p.push_back(big);
letter[big].in++;
int key = topSort(total);
if (key == -)
{
printf("Inconsistency found after %d relations.\n", i);
flag = true;
}
else if (key == )
{
printf("Sorted sequence determined after %d relations: %s.\n", i, seq);
flag = true;
}
}
if (!flag)
printf("Sorted sequence cannot be determined.\n");
}
return ;
}

POJ2585(ZOJ2193)-Window Pains

  题意:给定一组出现在固定位置的预设窗口,求可否使得这些窗口按照先后次序得到给定样例的显示状态。

  题解:拓扑排序,用覆盖表示先后关系,若最终无环则可以按照一定次序得到样例,如果有环,那么就与覆盖这种单向关系矛盾。

 //拓扑排序-窗口覆盖问题
//单向关系问题
//Time:16Ms Memory:176K
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std; #define MAX 4 //预设窗口
struct Map {
int size; //覆盖个数
int num[MAX];
}map[MAX][MAX]; struct Area {
vector<int> cover;
int in; //入度
}area[]; int win[MAX][MAX]; //实际窗口
int mov[][] = { {,}, {,}, {,}, {,} }; void init()
{
for (int i = ; i < ; i++)
for (int j = ; j < ; j++)
for (int k = ; k < ; k++)
{
int tx = i + mov[k][];
int ty = j + mov[k][];
map[tx][ty].num[map[tx][ty].size++] = i * + j + ;
}
} //拓扑排序
bool topology()
{
for (int i = ; i < ;i++)
{
int cur = ;
while (++cur < && area[cur].in); //找出入度为0的点
if (cur == ) return false; //存在环-非单向关系
area[cur].in--; vector<int> cover = area[cur].cover;
for (int i = ; i < cover.size(); i++)
area[cover[i]].in--;
}
return true;
} int main()
{
init(); char command[];
while (scanf("%s", command), strcmp(command, "ENDOFINPUT"))
{
memset(area, , sizeof(area));
for (int i = ; i < ; i++)
for (int j = ; j < ; j++)
scanf("%d", &win[i][j]);
scanf("%s", command); //Area-Cover
for (int i = ; i < ; i++)
for (int j = ; j < ; j++)
for (int k = ; k < map[i][j].size; k++)
{
vector<int> cover = area[win[i][j]].cover;
int num = map[i][j].num[k];
if (num != win[i][j] && cover.end() == find(cover.begin(), cover.end(), num)) //去重
{
area[win[i][j]].cover.push_back(num);
area[num].in++;
}
} if (topology())
printf("THESE WINDOWS ARE CLEAN\n");
else printf("THESE WINDOWS ARE BROKEN\n");
} return ;
}