hdu3639 Hawk-and-Chicken

本文介绍了一种利用强连通分量(SCC)进行缩点,并结合反图来解决特定图论问题的方法。通过构建反图并计算每个缩点的出度,找出度为0的点进行深度优先搜索,从而找到最大覆盖节点数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

刚看到此题, 感觉很简单,就顺着感觉写了。 后来,写不下去啦, 整不清楚了, 网上看了一下, 要建反图,

思路: 先建图, 后缩点,后根据缩后的点, 二次建图,但要建反图, 记录个缩点出度, 找出度为0 的, 进行一次dfs,

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>

using namespace std;

const int M = 50005;
struct node {

    int to;
    int next;

}num[M*2], num1[M];

int ins[M];
int head1[M];
int nod[M];
int belong[M];
int dfn[M];
int low[M];
int rdu[M];
int sccf[M];
int head[M];
int sum[M];
int vist[M];
stack<int>s;
int index, cont;
int ans;
int T, n, m, e, e1;

void init() {

    e = 0;
    index = 1;
    cont = 0;
    e1 = 0;
    memset(num, 0, sizeof(num));
    memset(head, -1, sizeof(head));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(sccf, 0, sizeof(sccf));
    memset(rdu, 0, sizeof(rdu));
    memset(sum, 0, sizeof(sum));
    memset(head1, -1, sizeof(head1));
    memset(nod, 0, sizeof(nod));
    memset(belong, 0, sizeof(belong));

}
void add(int a, int b) {

    num[e].to = b;
    num[e].next = head[a];
    head[a] = e++;
}

void add1(int a, int b) { //建反图,

    num1[e1].to = b;
    num1[e1].next = head1[a];
    head1[a] = e1++;
}

void Tanjian(int u) {  //缩点,

    int v;
    dfn[u] = low[u] = index++;
    ins[u] = 1;
    s.push(u);
    for(int k = head[u]; k != -1; k = num[k].next) {

        v = num[k].to;
        if(!dfn[v]) {

            Tanjian(v);
            low[u] = min(low[u], low[v]);
        }
        else if(ins[v]) {

            low[u] = min(low[u], low[v]);
        }
    }
    if(low[u] == dfn[u]) {

        cont++;
        do {

            v = s.top();
            s.pop();
            sccf[v] = cont;
            belong[cont]++; //存储没给缩点所包函的节点数,
            ins[v] = 0;

        }while(u != v);
    }

}

void dfs(int u, int p) {

    int v;
    for(int k = head1[u]; k != -1; k = num1[k].next) {

        v = num1[k].to;
        if(vist[v] != p) {

            nod[p] += belong[v];
            vist[v] = p; //记录v点是否遍历过,
            dfs(v, p);
        }
    }
}

void getdu() {

    for(int i = 0; i < n; i++) {

        for(int k = head[i]; k != -1; k = num[k].next) {

            int v = num[k].to;
            if(sccf[i] != sccf[v]) { //建反图

               add1(sccf[v], sccf[i]);
               rdu[sccf[i]]++;  //存储出度,
            }
        }
    }
      ans = 0;
    for(int i = 1; i <= cont; i++) {
        memset(vist, 0, sizeof(vist));
        if(rdu[i] == 0) {

           vist[i] = i;
           nod[i] = belong[i] -1;
           dfs(i, i);  
           ans = max(ans, nod[i]);
        }
    }

}

int main()
{
  int a, b, q = 1;
  scanf("%d", &T);

  while(T--) {

    scanf("%d%d", &n, &m);
    init();
    for(int i = 0; i < m; i++) {

        scanf("%d%d", &a, &b);
        if(a == b)
            continue;
           add(a, b);
    }
    for(int i = 0; i < n; i++) {

        if(!dfn[i]) {

            Tanjian(i);
        }
    }
    getdu();
    int flag = 0;
    printf("Case %d: %d\n", q++, ans);
   for(int i = 0; i < n; i++) {

     if(nod[sccf[i]] == ans) {

        if(!flag) {
            printf("%d", i);
            flag = 1;
        }
        else
            printf(" %d", i);
    }
  }
  printf("\n");

  }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值