UVA-1572-自组合

思路:
把字符看做点,把正方形看做边,建立有向图,拓扑排序,寻找有向环,若有环则可以无限延伸,否则不可以。
具体来说,每个点包含2个字符,一个是(A-Z),另一个是(+,-),(有一个特殊情况是00,我们不考虑,因为它不和任何一个点相连)。A+,A-,B+,B-…一共52个,分别分配下标

// 后面带+号的下标为偶数,-号的为奇数 
int ID( char x1, char x2 ) {
	return (x1-'A')*2 + (x2=='+' ? 0 : 1 );
}

依次读入边。组成边的两个点不是某一个正方形内的两个点,而是两个正方形各出一个点。比如A+在正方形S,其他3个点依次是B+,C+,00,那么A+要连的点不是B+,C+而是B+,C+可以向外延伸的点(另外一个正方形M的B-,C-点)。
那么如何快速得知B+,C+的可连的点(B-,C-)对应的下标呢? 亦或^。
数学知识: 偶数的亦或为自身+1,奇数的亦或为自身-1.
而0,1 对应A+,A-; 2,3对应B+,B-……
通过亦或可以快速确定其对应字母的下标。

void connect( char x1, char x2, char y1, char y2 ) {
	if( x1=='0'||y1=='0' ) return;
	int u = ID( x1,x2 );
	int v = ID( y1,y2 ) ^ 1;
	G[u][v] = 1;
}

剩下的就是dfs拓扑排序寻找有向环了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>

using namespace std;
const int N = 52;

int G[N][N], vis[N];
int n;

// 带+号的id为偶数,-号的为奇数 
int ID( char x1, char x2 ) {
	return (x1-'A')*2 + (x2=='+' ? 0 : 1 );
}

void connect( char x1, char x2, char y1, char y2 ) {
	if( x1=='0'||y1=='0' ) return;
	int u = ID( x1,x2 );
	int v = ID( y1,y2 ) ^ 1;
	G[u][v] = 1;
}

//拓扑排序的dfs 
bool dfs( int u ) {
	vis[u] = -1;
	for( int v=0; v<52; ++v ) {
		if( G[u][v] ) {
			if( vis[v]<0 ) return true;
			else if( 0==vis[v]&&dfs(v) ) return true;
		}
	}
	vis[u] = 1;
	return false;
}
//寻找有向图是否有环 
bool Find_circle() {
	memset( vis,0,sizeof(vis) );
	for( int u = 0; u<52; ++u ) {
		if( !vis[u] ) {
			if( dfs(u) ) return true;
		}
	}
	return false;
}

int main()
{
	
	while( cin>>n&&n ) {
		memset( G,0,sizeof(G) );
		char s[9];
		while( n-- ) {
			cin>>s;
			for( int i=0; i<4; ++i ) {
				for( int j=0; j<4; ++j ) {
					if( i!=j ) connect( s[i*2],s[i*2+1],s[j*2],s[j*2+1] );
				}
			}
			
		}
		if( Find_circle() ) cout<<"unbounded\n";
		else cout<<"bounded\n";
	}
	return 0;
}



猜你喜欢

转载自blog.csdn.net/CY05627/article/details/86758398