[CSU 1802 小X的战斗力] 拓扑排序+Floyd

[CSU 1802 小X的战斗力] 拓扑排序+Floyd

题目链接CSU 1802 小X的战斗力
题意描述:给定一个N个顶点,M条边的有向图。边A,B表示A>B。首先要判断是不是拓扑图。如果是,然后在求出的拓扑序列中,有几个顶点是大小确定的。即可以确定这个点是第几大。
解题思路:先用拓扑排序确定是否有环,有环输出“Wrong”, 然后根据Floyd求出任意两个顶点的大小关系。一个顶点在拓扑序列中位置是确定的当且仅当小于这个顶点的数目+大于这个顶点的数目==N-1。
判断是否有环,也可以用Floyd来做,我用dp[i][j] = 1表示i > j,如果存在dp[i][j] 并且dp[j][i]都为1,那么表示有环。

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

//#pragma comment(linker, "/STACK:1024000000,1024000000")

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
#define fst             first
#define snd             second

typedef long long LL;
typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;
const int MAXN = 150 + 5;
const int MAXE = 5000 + 5;

struct Edge {
    int v, next;
    Edge() {}
    Edge (int v, int next) : v (v), next (next) {}
} edges[MAXE];
int T, N, M;
int head[MAXN], ESZ, IN[MAXN];
int rk_X, rk_tot;
bool used[MAXN];
bool dp[MAXN][MAXN];
queue<int> Q;
void init() {
    ESZ = 0;
    memset (head, -1, sizeof (head) );
    memset (IN, 0, sizeof (IN) );
    memset (used, false, sizeof (used) );
    memset (dp, false, sizeof (dp) );
}
void add_edge (int u, int v) {
    edges[ESZ] = Edge (v, head[u]);
    head[u] = ESZ ++;
}
bool topu() {
    int u, v, cnt = 0;
    while (!Q.empty() ) Q.pop();
    for (u = 1; u <= N; u++) {
        if (!IN[u]) {
            used[u] = true;
            cnt ++;
            Q.push (u);
        }
    }
    while (Q.empty() ) return false;
    while (!Q.empty() ) {
        u = Q.front();
        Q.pop();
        for (int i = head[u]; ~i; i = edges[i].next) {
            v = edges[i].v;
            IN[v] --;
            if (!IN[v]) {
                if (used[v]) return false;
                used[v] = true;
                cnt ++;
                Q.push (v);
            }
        }
    }
    return cnt == N;
}
int main() {
#ifndef ONLINE_JUDGE
    FIN;
#endif // ONLINE_JUDGE
    int u, v;
    scanf ("%d", &T);
    while (T --) {
        init();
        scanf ("%d %d", &N, &M);
        for (int i = 1; i <= M; i++) {
            scanf ("%d %d", &u, &v);
            add_edge (u, v);
            dp[u][v] = 1;
            IN[v] ++;
        }
        bool suc = topu();
        if (!suc) {
            printf ("Wrong\n");
        } else {
            for (int k = 1; k <= N; k++) {
                for (int i = 1; i <= N; i++) {
                    if (!dp[i][k]) continue;
                    for (int j = 1; j <= N; j++) {
                        if (!dp[k][j])  continue;
                        dp[i][j] |= dp[i][k] && dp[k][j];
                    }
                }
            }
            rk_tot = 0, rk_X = -1;
            for (int i = 1; i <= N; i++) {
                /// sum1表示小于i的顶点个数;sum2表示大于i的顶点个数
                int sum1 = 0, sum2 = 0;
                for (int j = 1; j <= N; j++) {
                    if (dp[i][j]) sum1 ++;
                    if (dp[j][i]) sum2 ++;
                }
                if (sum1 + sum2 + 1 == N) {
                    rk_tot ++;
                    if (i == 1) rk_X = sum2 + 1;
                }
            }
            printf ("%d\n%d\n", rk_X, rk_tot);
        }
    }
    return 0;
}
/// 思路二
#include <bits/stdc++.h>
using namespace std;

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
#define fst             first
#define snd             second

typedef long long LL;
typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;
const int MAXN = 150 + 5;
const int MAXE = 5000 + 5;

int T, N, M;
int rk_X, rk_tot;
bool dp[MAXN][MAXN];

void init() {
    memset (dp, false, sizeof (dp) );
}
bool Floyd() {
    for (int k = 1; k <= N; k++) {
        for (int i = 1; i <= N; i++) {
            if (!dp[i][k]) continue;
            for (int j = 1; j <= N; j++) {
                if (!dp[k][j])  continue;
                dp[i][j] |= dp[i][k] && dp[k][j];
            }
        }
    }
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) {
            if (dp[i][j] && dp[j][i]) return false;
        }
    }
    return true;
}
int main() {
#ifndef ONLINE_JUDGE
    FIN;
#endif // ONLINE_JUDGE
    int u, v;
    scanf ("%d", &T);
    while (T --) {
        init();
        scanf ("%d %d", &N, &M);
        for (int i = 1; i <= M; i++) {
            scanf ("%d %d", &u, &v);
            dp[u][v] = true;
        }
        bool suc = Floyd();
        if (!suc) {
            printf ("Wrong\n");
        } else {
            rk_tot = 0, rk_X = -1;
            for (int i = 1; i <= N; i++) {
                /// sum1表示小于i的顶点个数;sum2表示大于i的顶点个数
                int sum1 = 0, sum2 = 0;
                for (int j = 1; j <= N; j++) {
                    if (dp[i][j]) sum1 ++;
                    if (dp[j][i]) sum2 ++;
                }
                if (sum1 + sum2 + 1 == N) {
                    rk_tot ++;
                    if (i == 1) rk_X = sum2 + 1;
                }
            }
            printf ("%d\n%d\n", rk_X, rk_tot);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值