Potato的暑期训练day#2 ——图论之拓扑排序

Potato的暑期训练day#2 ——图论之拓扑排序

题目链接:

A.http://poj.org/problem?id=2585

B.http://poj.org/problem?id=1270

C.http://poj.org/problem?id=1094

D.http://poj.org/problem?id=3687

A.Window Pains

题意:已知一个4*4的方格和各方格可能出现的数字,给出一个方格当前的显示,求出达到当前情况所需要的方格覆盖次序。

思路:先打出各个方格可能出现数字的表,然后根据当前方格的数字来确定该数在哪些数的上面,与之连边建立有向图拓扑排序即可。

代码:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N = 4;
int G[N][N];
int deg[9],s;
vector<int> F[N*N];
int PO[9][9];
void init() {
    F[0].push_back(1);
    F[1].push_back(1);F[1].push_back(2);
    F[2].push_back(2);F[2].push_back(3);
    F[3].push_back(3);
    F[4].push_back(1);F[4].push_back(4);
    F[5].push_back(1);F[5].push_back(2);F[5].push_back(4);F[5].push_back(5);
    F[6].push_back(2);F[6].push_back(3);F[6].push_back(5);F[6].push_back(6);
    F[7].push_back(3);F[7].push_back(6);
    F[8].push_back(4);F[8].push_back(7);
    F[9].push_back(4);F[9].push_back(5);F[9].push_back(7);F[9].push_back(8);
    F[10].push_back(5);F[10].push_back(6);F[10].push_back(8);F[10].push_back(9);
    F[11].push_back(6);F[11].push_back(9);
    F[12].push_back(7);
    F[13].push_back(7);F[13].push_back(8);
    F[14].push_back(8);F[14].push_back(9);
    F[15].push_back(9);
} 
bool top(int s) {
    int flag = 0;
    queue<int> Q;
    while(!Q.empty()) Q.pop();
    for(int i = 0;i < 9;i++) {
        if(deg[i] == 0) Q.push(i);
    }
    while(!Q.empty()) {
        int u = Q.front();Q.pop();
        for(int i = 0;i < 9;i++) {
            if(PO[u][i]) {
                deg[i]--;
                s--;
                if(!deg[i]) {
                    Q.push(i);
                }
            }
        }
    }
    if(s == 0) return true;
    else return false;
}
bool solve() {
    for(int i = 0;i < N;i++) {
        for(int j = 0;j < N;j++) {
            int u = N * i + j,v = G[i][j];
            for(int k = 0;k < F[u].size();k++) {
                if(F[u][k] != v) {
                    if(PO[v-1][F[u][k]-1] == 0){
                        deg[F[u][k]-1]++;
                        PO[v-1][F[u][k]-1] = 1;
                        s++;
                    }
                }
            }
        }
    }
    /*for(int i = 0;i < 9;i++) {
        for(int j = 0;j < 9;j++) {
            printf("%d ",PO[i][j]);
        }
        printf("\n");
    }*/
    //printf("%d\n",s);
    if(top(s)) return true;
    else return false;
}
int main() {
    char str[20];
    init();
    for(;;) {
        scanf("%s",str);
        if(strlen(str) > 3 && str[3] == 'O') break;
        memset(deg,0,sizeof(deg));
        memset(PO,0,sizeof(PO));
        s = 0;
        for(int i = 0;i < N;i++) 
            for(int j = 0;j < N;j++) 
                scanf("%d",&G[i][j]);
        if(solve()) printf("THESE WINDOWS ARE CLEAN\n");
        else printf("THESE WINDOWS ARE BROKEN\n");
        scanf("%s",str);
    }
    return 0;
}

B.Following Orders

题意:给定变量之间形如x<y的约束关系列表,要求按字典序输出满足约束关系的所有变量序列

思路:由于要输出所有满足约束关系的序列,所以使用dfs回溯的方法优先搜索字典序小的

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<ctype.h>
#include<map>
using namespace std;
char s[100];
char tt[30];
int G[30][30];
int deg[30];
int cnt,cur;
char ans[30];
map<char,int> mp;
void topsort(int depth) {
    if(depth == cnt) {printf("%s\n",ans);return ;}
    for(int i = 0;i < cnt;i++) {
        if(deg[i] == 0) {ans[depth] = tt[i];
        --deg[i];
        for(int j = 0;j < cnt;j++) {
            if(G[i][j]) deg[j]--;
        }
        topsort(depth+1);
        ++deg[i];
        for(int j = 0;j < cnt;j++) {
            if(G[i][j]) deg[j]++;
        }
    }}
}
int main() {
    for(;;) {
        gets(s);
        int len = strlen(s);
        if(len == 0) break;
        cnt = 0;
        memset(tt,0,sizeof(tt));
        memset(G,0,sizeof(G));
        memset(deg,0,sizeof(deg));
        memset(ans,0,sizeof(ans));
        for(int i = 0;i < len;i++) {
            if(isalpha(s[i])) {
                tt[cnt++] = s[i];
            } 
        }
        sort(tt,tt+cnt);
        for(int i = 0;i < cnt;i++) {
            mp[tt[i]] = i;
        }
        memset(s,0,sizeof(s));
        gets(s);
        len = strlen(s);
        if(len == 0) break;
        int flag = 0;
        int u,v;
        for(int i = 0;i < len;i++) {
            if(isalpha(s[i]) && !flag) {
                u = mp[s[i]];
                flag = 1;
            }
            else if(isalpha(s[i]) && flag) {
                v = mp[s[i]];
                flag = 0;
                G[u][v] = 1;
                deg[v]++;
            }
        }
       topsort(0);
       printf("\n");
    }
    return 0;
}

C.Sorting It All Out

题意:将需要拓扑排序的边一条一条的给你,让你判断是否可以确定这个序列只有一种情况,还是有多种情况,还是有环。

思路:bfs判断是否有环,维护入度为0的点的时候,观察是否存在超过1的情况。

代码:

#include<vector>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 27;
int n,m;
vector<int> G[maxn];
int in[maxn],deg[maxn];
char ans[maxn];
int cnt;
void init() {
    for(int i = 0;i < n;i++) G[i].clear();
    memset(in,0,sizeof(in));
}
bool check(int u,int v) {
    for(int i = 0;i < G[u].size();i++) if(G[u][i] == v) return true;
    return false;
}
//传入当前边数
int topsort() {
    for(int i = 0;i < n;i++) deg[i] = in[i];
    cnt = 0;
    int flag = 1;
    memset(ans,0,sizeof(ans));
    queue<int> Q;
    while(!Q.empty()) Q.pop();
    for(int i = 0;i < n;i++) {
        if(deg[i] == 0) Q.push(i);
    }
    //if(Q.size() > 1) flag = 0; !!!判断是否有多种情况
    while(!Q.empty()) {
        if(Q.size() > 1) flag = 0; 
        int u = Q.front();Q.pop();
        ans[cnt++] = u + 'A';
        int len = G[u].size();
        for(int i = 0;i < len;i++) {
            int v = G[u][i];
            deg[v]--;
            if(deg[v] == 0) {
                Q.push(v);
            }
        }
    }
    if(cnt < n) return 2;
    else if(cnt == n && flag) return 3;
    else return 1; 
}
int main() {
    while(scanf("%d%d",&n,&m) == 2) {
        if(n == 0 && m == 0) break;
        init();
        char s[5];
        int flag,ok = 0,k;  
        for(int i = 0;i < m;i++) {
            scanf("%s",s);
            if(ok) continue;
            int u = s[0] - 'A',v = s[2] - 'A';
            if(check(u,v)) continue;
            in[v]++;
            G[u].push_back(v);
            flag = topsort();
            if(flag == 3) {
                ok = 1;
                printf("Sorted sequence determined after %d relations: %s.\n",i+1,ans);
            }
            else if(flag == 2) {
                ok = 1;
                printf("Inconsistency found after %d relations.\n",i+1);
            }
        }
        if(flag == 1) printf("Sorted sequence cannot be determined.\n");
    }
    return 0;
}

D.Labeling Balls

题意:有N个不同质量的球,重量为1~N,现在满足一些约束(类似于编号a的球比b的轻) 来给这些球贴1-N的标签,如果有多种情况要保证序号小的球质量轻。

思路:我们先考虑对位置的排序,我们按照1,2,3...N的质量来放球,我们要调整位置的排序使之满足,比如说我们有a<b,从c<d那么我们要保证a的位置在b前面(c,d同理),其次我们要满足序号小的尽量要小,我们放置a和c的时候可能会将a,c较小的贪心放前面,但是我们并不知道b,d是否更小来使得情况更优,所以我们考虑逆向的拓扑排序,我们先来放b,d这样就不会构成矛盾了

代码:

#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 205;
int n,m,cnt;
int deg[maxn];
int ans[maxn];
vector<int> G[maxn];
void topsort() {
    int t = n;
    priority_queue<int> Q;
    while(!Q.empty()) Q.pop();
    for(int i = 1;i <= n;i++) if(!deg[i]) Q.push(i);
    while(!Q.empty()) {
        int u = Q.top();Q.pop();
        ans[u] = t--;
        for(int i = 0;i < G[u].size();i++) {
            int v = G[u][i];
            deg[v]--;
            if(!deg[v]) Q.push(v);
        }
    }
    if(!t) {
        for(int i = 1;i <= n;i++) printf("%d ",ans[i]);
        printf("\n"); 
    }
    else printf("-1\n");
}
bool check(int u,int v) {
    for(int i = 0;i < G[u].size();i++) {
        int t = G[u][i];
        if(t == v) return false;
    }
    return true;
}
void init() {
    memset(ans,0,sizeof(ans));
    memset(deg,0,sizeof(deg));
    for(int i = 1;i<=n;i++) G[i].clear();
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&m);
        init();
        int u,v;
        for(int i = 0;i < m;i++) {
            scanf("%d%d",&u,&v);
            if(check(v,u)) {
                G[v].push_back(u);
                deg[u]++;
            }
        }
        topsort();
    }
    return 0;
}

转载于:https://www.cnblogs.com/pot-a-to/p/11139337.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值