拓扑排序的经典题目 UVA1572

紫书172的例题:

题目大意:有n种正放形,每种正方形的数量可视为无限多。已知边与边之间的结合规则,而且正方形可以任意旋转和反转,问这n中正方形是否可以拼成无限大的图案

思路:首先因为是要无穷大,所以只要正方形之间的连接出现有向环及说明可以无限。但是如果单纯的对正方形进行枚举肯定会TLE,因此我们换一个思路。

首先有没有正方形是无所谓的,如果有一个是A-另外一个正方形里面有A+,管他是哪个正方形,只要能连过去就行了,反正只要最后如果能连回来就说明有环。

所以我们把所有的字母看成点,然后让正方形中每个字母进行连线即可。最后利用拓扑序和染色法求解是否存在环即可。

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>

#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
const int maxn = 40000 + 5;
int n;
bool G[100][100];

int cal(char a, char b){
    int res = (a - 'A') * 2 + (b == '+' ? 0 : 1);
    return res;
}

void connect(char a1, char a2, char b1, char b2){
    int u = cal(a1, a2) ^ 1, v = cal(b1, b2);
    G[u][v] = 1;
}

int vis[100];
bool dfs(int u){
    vis[u] = -1;
    for (int i = 0; i < 52; i++){
        if (G[u][i]){
            if (vis[i] < 0) return true;
            else if(!vis[i] && dfs(i)){
                return true;
            }
        }
    }
    vis[u] = 1;
    return false;
}

bool find_cir(){
    memset(vis, 0, sizeof(vis));
    for (int i = 0; i < 52; i++){
        if (!vis[i] && dfs(i)) return true;
    }
    return false;
}

int main(){
    while (scanf("%d", &n) == 1 && n){
        memset(G, false, sizeof(G));
        char ch[10];
        for (int i = 1; i <= n; i++){
            scanf("%s", ch);
            for (int i = 0; i < 4; i++){
                for (int j = 0; j < 4; j++){
                    if (i == j || ch[i * 2] == '0' || ch[j * 2] == '0') continue;
                    connect(ch[i * 2], ch[i * 2 + 1], ch[j * 2], ch[j * 2 + 1]);
                }
            }
        }
        if (find_cir()) printf("unbounded
");
        else printf("bounded
");
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/heimao5027/p/5790629.html