拓撲排序整理

POJ1094

題意:就是給出一些字母的大小關系, 問你能不能確定每一個字母的大小

這題有個隱藏的東西: whichever comes first, 這句話的意思就是先有序就輸出有序, 先有矛盾就先輸出矛盾, 後面的數據就不用在處理了, 如果處理到最後一組關系都不能確定是否有序和矛盾, 那麼就是無法確定順序.

這裏有一點非常有意思, 邏輯上得特別嚴謹, 我最開始判斷如果無序, 也就是說隊列中入度爲的點超過一個, 那麼就是無序直接返回無序的特徵值.

很顯然這是不對的額, 無序的情況可能還包含着矛盾 就比如兩個聯通塊, 肯定無序, 但是某一個聯通塊如果存在矛盾, 那麼就要輸出矛盾, 直接返回無序的結果就是錯誤的.

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <set>
#include <queue>

using namespace std;
typedef long long ll;
int gra[30][30];
int deg[30];
int degree[30];
int res[30];
int n, m;
int VOA(){
	queue<int> Q;
	for(int i = 1; i <= n; ++i){
		degree[i] = deg[i];
		if(deg[i] == 0)	Q.push(i);
	}
	int flag = 0;
	int cnt = 0;
	while(!Q.empty()){
		if(Q.size() > 1)	flag = -1;	// remark thie is not sort.
		int u = Q.front();	Q.pop();
		res[cnt++] = u;
		for(int i = 1; i <= n; ++i)
			if(gra[u][i]){
				degree[i]--;
				if(degree[i] == 0)	Q.push(i);
			}
	}
	int k = 0;
	if(cnt == n && flag == 0)	return -1;	//special judge whether sort.
	for(int i = 1; i <= n; ++i)
		if(degree[i] != 0)	k++;
	return k;
}

int main(){
	while(scanf("%d%d", &n, &m) != EOF){
		if(n == 0 && m == 0)	break;
		memset(gra, 0, sizeof(gra));
		memset(deg, 0, sizeof(deg));
		bool flag = false;
		char op[10];
		for(int i = 0; i < m; ++i){
			scanf("%s", op);
			if(flag)	continue;
			int x = op[0] - 'A' + 1;
			int y = op[2] - 'A' + 1;
			if(op[1] == '>'){
				gra[x][y] = 1;
				deg[y]++;
			}else{
				gra[y][x] = 1;
				deg[x]++;
			}
			int ans = VOA();
			if(ans > 0){
				printf("Inconsistency found after %d relations.\n", i + 1);
				flag = true;
				continue;
			}else if(ans == -1){
				printf("Sorted sequence determined after %d relations: ", i + 1);
				for(int i = n - 1; i >= 0; --i)
					printf("%c", res[i] + 'A' - 1);
				puts(".");
				flag = true;
				continue;
			}
		}
		if(!flag)	printf("Sorted sequence cannot be determined.\n");
	}
	return 0;
}


POJ1041

題意:從路口a進入街道x然後從b出口出來.問是否存在從他所在的路口出發訪問每一個街道一次並回到自己所在的路口,存在就輸出訪問的字典序最小的街道序列.

很顯然如果存在一個歐拉圖肯定就存在這樣的路徑.

但是難點在於輸出路徑, 這裏我又犯了一個片面的錯誤, 就是每次找它能連接到的最小的街道然後走, 殊不知如果走環是最小的那麼有些點就訪問不到了.

所以這裏借用dfs的先搜環, 然後在存點.

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

const int qq = 2005;
int gra[51][2005];
int vis[qq];
int deg[qq];
int res[qq];
int a, b, c, maxn, sta;
void Action(int x, int y, int z){
	deg[x]++, deg[y]++;
	gra[x][z] = y;
	gra[y][z] = x;
	maxn = max(maxn, c);
}
void dfs(int u, int cnt){
	if(cnt == maxn)	return;
	for(int i = 1; i <= maxn; ++i){
		if(!vis[i] && gra[u][i] != 0){
			vis[i] = 1;
			gra[u][i] = gra[gra[u][i]][u] = 0;
			res[cnt] = i;
			dfs(i, cnt + 1);
		}
	}
}

int main(){
	while(scanf("%d%d", &a, &b) == 2){
		if(a == 0 && b == 0)	break;
		memset(deg, 0, sizeof(deg));
		memset(gra, 0, sizeof(gra));
		memset(vis, 0, sizeof(vis));
		sta = max(a, b);
		maxn = 0;
		scanf("%d", &c);
		Action(a, b, c);
		while(scanf("%d%d", &a, &b) == 2){
			if(a == 0 && b == 0)	break;
			scanf("%d", &c);
			Action(a, b, c);
		}
		bool flag = true;
		for(int i = 1; i <= maxn; ++i)
			if(deg[i] % 2 == 1)	flag = false;
		if(!flag){
			puts("Round trip does not exist.");
			continue;
		}
		dfs(sta, 0);
		for(int i = 0; i < maxn - 1; ++i)
			printf("%d ", res[i]);
		printf("%d\n", res[maxn - 1]);
	}
	return 0;
}



POJ1128

題意:給出一個n*m的矩形相框, 這是一些其他相框疊加起來後的結果, 每一個相框都只有一種字母,.相框是從底部向上放, 上方的相框的字母會覆蓋掉下面的字母, 題目保證每一種字母相框的四條邊至少會出現一個點, 不存在相同字母的相框. 問底部到頂部的覆蓋字母框的順序.如果有多種按字典序輸出.


難就難在建圖, 最開始不知道怎麼確定每一個字母框, 其實可以根據   "目保證每一種字母相框的四條邊至少會出現一個點," , 可以根據這個確定每一種字母的框架大小, 然後根據裏面出現的與他不同的字母, 就知道這個不同的字母是覆蓋在它上面的, 所以就有一條該字母指向與它不同字母的一條邊, 這題目其實有點玄乎, 應爲26個字母的排列結果有26!  dfs枚舉起來是2^26次方...  但是題目主要考察的還是建圖,  但是還是要注意要考慮到這種極端數據.確定矩形找到左上角和右下角就可以了

#include <cstdio>
#include <cstring>

using namespace std;
const int maxn = 32;
const int maxm = 28;

char ori[maxn][maxn], ans[maxm];
int n, m, in[maxm], total;
bool map[maxm][maxm];
struct Node{
	int x, y;
}lt[maxm];
struct Node2{
	int x, y;
}rb[maxm];

void getMap(){
	int i, j, t, x, y;
	memset(map, 0, sizeof(map));
	memset(in, -1, sizeof(in));
	memset(lt, 0x3f, sizeof(lt));
	memset(rb, -1, sizeof(rb));
	for(i = total = 0; i < n; ++i)
		for(j = 0; j < m; ++j){
			if(ori[i][j] == '.')	continue;
			t = ori[i][j] - 'A';
			if(in[t] == -1){
				in[t] = 0;
				++total;
			}
			if(i < lt[t].x)	lt[t].x = i;
			if(i > rb[t].x)	rb[t].x = i;
			if(lt[t].y > j)	lt[t].y = j;
			if(rb[t].y < j)	rb[t].y = j;
		}
	for(i = 0; i < maxm; ++i){
		if(in[i] == -1)	continue;
		for(x = lt[i].x; x <= rb[i].x; ++x)
			for(y = lt[i].y; y <= rb[i].y; ++y){
				if(x > lt[i].x && y > lt[i].y && x < rb[i].x && y < rb[i].y)
					continue;
				t = ori[x][y] - 'A';
				if(t != i && !map[i][t]){
					map[i][t] = true;
					++in[t];
				}
			}
	}
}
void DFS(int id){
	if(id == total){
		ans[id] = '\0';
		puts(ans);
		return;
	}
	for(int i = 0; i < maxm; ++i){
		if(in[i] == 0){
			ans[id] = 'A' + i;
			in[i] = -1;
			for(int j = 0; j < maxm; ++j)
				if(map[i][j])	--in[j];
			DFS(id + 1);
			in[i] = 0;
			for(int j = 0; j < maxm; ++j)
				if(map[i][j]) ++in[j];
		}
	}
}

int main(){
	int i;
	while(scanf("%d%d", &n, &m) == 2){
		for(i = 0; i < n; ++i)
			scanf("%s", ori[i]);
		getMap();
		DFS(0);
	}
}


HDU2762

題意:給出一個圖, 問世否可以任意選擇兩個點, u, v, 至少存在一條路徑使得能從u到v或者從v到u

要滿足給定的條件, 最基礎的是這麼多個點進行拓撲排序的結果只有一種, 也就是形成一條鏈.

很顯然, 圖中存在環, 這個時候環我們可以堪稱是一個點, 所以我們可以用Tarjan算法進行縮點.

然後進行拓撲排序, 拓撲排序的結果要唯一, 這一點要特別判斷.

特別注意:不能在Tarjan算法之後 直接判斷這個圖的入度爲0的點只存在一個, 很顯然這樣做是錯誤的,

比如這樣的數據

a->b

a->c

c->d

b->d

很明顯b, c之間是沒有路徑的.

所以我們還是得判斷拓撲排序的結果唯一或者之只形成一條連

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

const int qq = 2005;
int head[qq];
int dfn[qq], low[qq], belong[qq], stack[qq], indeg[qq], outdeg[qq];
int tot, Index, n, m, Bcnt, top;
bool instack[qq];
vector<int> vt[qq];
struct Node{
	int v;
	int next;
}edge[qq * 10];
void Add(int u, int v){
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}
void Init(){
	top = tot = Index = Bcnt = 0;
	for(int i = 0; i <= n; ++i)	vt[i].clear();
	memset(dfn, 0, sizeof(dfn));
	memset(head, -1, sizeof(head));
	memset(instack, false, sizeof(instack));
	memset(indeg, 0, sizeof(indeg));
	memset(outdeg, 0, sizeof(outdeg));
}

void Tarjan(int u){
	int v;
	dfn[u] = low[u] = ++Index;
	stack[++top] = u;
	instack[u] = true;
	for(int i = head[u]; i != -1; i = edge[i].next){
		v = edge[i].v;
		if(!dfn[v]){
			Tarjan(v);
			if(low[v] < low[u])
				low[u] = low[v];
		}else if(instack[v] && dfn[v] < low[u]){
			low[u] = dfn[v];
		}
	}
	if(low[u] == dfn[u]){
		++Bcnt;
		do{
			v = stack[top--];
			instack[v] = false;
			belong[v] = Bcnt;
		}while(v != u);
	}
}
void VOA(){
	queue<int> Q;
	for(int i = 1; i <= Bcnt; ++i)
		if(indeg[i] == 0)	Q.push(i);
	int cnt = 0;
	while(!Q.empty()){
		if(Q.size() > 1){
			puts("No");
			return;
		}
		int u = Q.front();	Q.pop();
		cnt++;
		for(int i = 0; i < (int)vt[u].size(); ++i){
			int v = vt[u][i];
			indeg[v]--;
			if(indeg[v] == 0)	Q.push(v);
		}
	}
	if(cnt == Bcnt)	puts("Yes");
	else	puts("No");
}

int main(){
	int t;	scanf("%d", &t);
	while(t--){
		Init();
		scanf("%d%d", &n, &m);
		int a, b;
		for(int i = 0; i < m; ++i){
			scanf("%d%d", &a, &b);
			if(a == b){
				i--, m--;
				continue;
			}
			Add(a, b);
		}
		for(int i = 1; i <= n; ++i)
			if(!dfn[i])	Tarjan(i);
		if(Bcnt == 1){
			puts("Yes");
			continue;
		}
		for(int i = 1; i <= n; ++i){
			int u = belong[i];
			for(int j = head[i]; j != -1; j = edge[j].next){
				int v = belong[edge[j].v];
				if(u == v)	continue;
				indeg[v]++;
				outdeg[u]++;
				vt[u].push_back(v);
			}
		}
		VOA();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值