P1347排序(Floyd传递闭包)

原题: 

Description

An ascending sorted sequence of distinct values is one in which some form of a less-than operator is used to order the elements from smallest to largest. For example, the sorted sequence A, B, C, D implies that A < B, B < C and C < D. in this problem, we will give you a set of relations of the form A < B and ask you to determine whether a sorted order has been specified or not.

Input

Input consists of multiple problem instances. Each instance starts with a line containing two positive integers n and m. the first value indicated the number of objects to sort, where 2 ≤ n ≤ 26. The objects to be sorted will be the first n characters of the uppercase alphabet. The second value m indicates the number of relations of the form A < B which will be given in this problem instance. Next will be m lines, each containing one such relation consisting of three characters: an uppercase letter, the character "<" and a second uppercase letter. No letter will be outside the range of the first n letters of the alphabet. Values of n = m = 0 indicate end of input.

Output

For each problem instance, output consists of one line. This line should be one of the following three:

Sorted sequence determined after xxx relations: yyy...y.

Sorted sequence cannot be determined.

Inconsistency found after xxx relations.

where xxx is the number of relations processed at the time either a sorted sequence is determined or an inconsistency is found, whichever comes first, and yyy...y is the sorted, ascending sequence.

Sample Input

4 6
A<B
A<C
B<C
C<D
B<D
A<B
3 2
A<B
B<A
26 1
A<Z
0 0

Sample Output

Sorted sequence determined after 4 relations: ABCD.
Inconsistency found after 2 relations.
Sorted sequence cannot be determined.

Source

East Central North America 2001

翻译:

Description

给定 n 个变量和 m 个不等式。其中 n 小于等于 26,变量分别用前 n 个大写英文字母表示。

不等式之间具有传递性,即若 A>B 且 B>C,则 A>C (2≤n≤26,变量只可能为大写字母 A∼Z)

请从前往后遍历每对关系,每次遍历时判断:

  • 如果能够确定全部关系且无矛盾,则结束循环,输出确定的次序;
  • 如果发生矛盾,则结束循环,输出有矛盾;
  • 如果循环结束时没有发生上述两种情况,则输出无定解。

Input

输入包含多组测试数据。

每组测试数据,第一行包含两个整数 n 和 m。

接下来 m 行,每行包含一个不等式,不等式全部为小于关系。

当输入一行 0 0 时,表示输入终止。

Output

每组数据输出一个占一行的结果。

结果可能为下列三种之一:

  1. 如果可以确定两两之间的关系,则输出 "Sorted sequence determined after t relations: yyy...y.",其中't'指迭代次数,'yyy...y'是指升序排列的所有变量。
  2. 如果有矛盾,则输出: "Inconsistency found after t relations.",其中't'指迭代次数。
  3. 如果没有矛盾,且不能确定两两之间的关系,则输出 "Sorted sequence cannot be determined."

具体思路:每读入一个不等式关系后就用Floyd求一次传递闭包,并判断是否矛盾或是否可以确定两两之间的关系,是的话则直接输出结果,否的话则继续以上步骤。

详细部分解析:

 Floyd求传递闭包的实现:如果i沿路径可以到达j,则令dist[i][j]=1,否则dist[i][j]=0。在这道题目中,我们将a<b的关系看成a发出一条指向b的边,则dist[a][b]=1。同理,如果b<c,则dist[b][c]=1,既然a<b且b<c,那么a可以沿路径走到c,因此dist[a][c]也等于1。

由上面的推论,我们只需将Floyd求最短路模板中dist[i][j] = min(dist[i][j] , dist[i][k] + dist[k][j])改成dist[i][j] = dist[i][k] && dist[k][j] 即可,即如果i通过k走到j,那么i可以到达j(i<k且k<j那么i<j)

但其实这么写是欠妥的,我们忽略了一种情况:i可以走到j,但是i无法通过k走到j:dist[i][j] = 1,但是dist[i][k]或dist[k][j]中有0的情况,则会将dist[i][j]也置为0,因此正确写法应该为dist[i][j] |= dist[i][k] && dist[k][j]

判断关系是否出现矛盾:如果i<j且j<i则会出现矛盾:i<i ,因此我们可以遍历所有节点,如果出现dist[i][i] = 1则说明出现矛盾情况

判断是否两两之间已确定关系:如果两两之间已经确定关系,对于任意两个顶点i,j必有i < j或j < i即dist[i][j]或dist[j][i]等于1,因此我们只需要判断所有点对是否满足该条件即可

按顺序输出字母:每次只需要输出在未被标记过的字母当中小于其它字母的字母即可,然后标记该字母

代码如下:

#include<bits/stdc++.h>
using namespace std;

#define N 30
int st[N], d[N][N], g[N][N];
int n, m;

void floyd() {

	memcpy(d, g, sizeof g);
	for (int k = 0; k < n; k++)
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				d[i][j] |= d[i][k] && d[k][j];

}

int check() {

	for (int i = 0; i < n; i++)//判断是否出现矛盾情况
		if (d[i][i])return 2;

	for (int i = 0; i < n; i++)//判断两两之间关系是否确定
		for (int j = i + 1; j < n; j++)
			if (!d[i][j] && !d[j][i])return 0;

	return 1;
}

char get_min() {
	
	for (int i = 0; i < n; i++) {
		int flag = 0;
		if (!st[i]) {
			for (int j = 0; j < n; j++) {
				if (!st[j] && d[j][i]) {
					flag = 1;
					break;
				}				
			}
			if (!flag) {
				st[i] = 1;
				return 'A' + i;
			}
		}
	}
}

char str[5];
int main(){
	int t;//记录轮次
	while (cin >> n >> m, n || m) {
		memset(g,0,sizeof g);
		int type=0;//记录答案类型
		for (int i = 1; i <= m; i++) {
			cin >> str;
			int a = str[0] - 'A', b = str[2] - 'A';
			
		    if(!type){
		        g[a][b] = 1;
			    floyd();
		        type=check();  
		        if(type)t=i;
		    } 
		    
		}
		if (type == 2)printf("Inconsistency found after %d relations.\n", t);
		else if (!type)printf("Sorted sequence cannot be determined.\n");
		else {
			memset(st, 0, sizeof st);
			printf("Sorted sequence determined after %d relations: ", t);
			for (int i = 0; i < n; i++)
				printf("%c", get_min());
			printf(".\n");
		}
		
	}

	return 0;
}

扩展优化: 我们可以尝试将三维缩成两维,在每次添加一个不等式关系a<b后,我们可以做以下操作:1.将“能到达a的点”与“b能到达的点”连接;2.将a与“b能到达的点”连接;3.将“能到达a的点”与b连接。

代码如下:

#include<bits/stdc++.h>
using namespace std;

#define N 30
int st[N], d[N][N];
int n, m;

int check() {

	for (int i = 0; i < n; i++)//判断是否出现矛盾关系
		if (d[i][i])return 2;

	for (int i = 0; i < n; i++)//判断是否已经确定两两关系
		for (int j = i + 1; j < n; j++)
			if (!d[i][j] && !d[j][i])return 0;

	return 1;
}

char get_min() {
	
	for (int i = 0; i < n; i++) {
		int flag = 0;
		if (!st[i]) {
			for (int j = 0; j < n; j++) {
				if (!st[j] && d[j][i]) {
					flag = 1;
					break;
				}				
			}
			if (!flag) {
				st[i] = 1;
				return 'A' + i;
			}
		}
	}
}

char str[5];
int main(){
	int t;//记录轮次
	while (cin >> n >> m, n || m) {
		memset(d,0,sizeof d);
		int type=0;//记录答案类型
		for (int i = 1; i <= m; i++) {
			cin >> str;
			int a = str[0] - 'A', b = str[2] - 'A';
			
		    if(!type){
		        d[a][b] = 1;
			    for(int x=0;x<n;x++){
			        if(d[x][a])d[x][b]=1;//将能到达a的点与b连接
			        if(d[b][x])d[a][x]=1;//将a与b能到达的点连接
			        for(int y=0;y<n;y++)//将能到达a的点与b能到达的点连接
			            if(d[x][a]&&d[b][y])d[x][y]=1;
			    }
			
		        type=check();  
		        if(type)t=i;
		    } 
		    
		}
		if (type == 2)printf("Inconsistency found after %d relations.\n", t);
		else if (!type)printf("Sorted sequence cannot be determined.\n");
		else {
			memset(st, 0, sizeof st);
			printf("Sorted sequence determined after %d relations: ", t);
			for (int i = 0; i < n; i++)
				printf("%c", get_min());
			printf(".\n");
		}
		
	}
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值