4269. 【NOIP2015模拟10.27】挑竹签

洛谷AC传送门

题目让我们求最多可以抽走多少个竹签。官方题解是建图然后求拓补排序。

首先,同样的,我们从上面的竹签向被压竹签建有向图。可以发现,不能抽走的竹签都是因为在环中。所以我们统计一下一个点的入度。

对于入度为$0$的点,我们将其放入队列中$bfs$。当然,我们从队列中每取出一个点,ans就要加$1$,因为这个入度为0的点是可以取走的。

同时,取出这个竹签后,其被压竹签的入度也相应减$1$,如果发现入度为$0$的竹签则继续丢入队列。

#include <bits/stdc++.h>
using namespace std;
#define N 1000010

inline int read(){
    int x = 0, s = 1;
    char c = getchar();
    while(!isdigit(c)){
        if(c == '-') s = -1;
        c = getchar();
    }
    while(isdigit(c)){
        x = x * 10 + (c ^ '0');
        c = getchar();
    }
    return x * s;
}

struct node{
    int v, next;
} t[N];
int f[N];

int bian = 0;
inline void add(int u, int v){
    t[++bian] = (node){v, f[u]}, f[u] = bian;
    return ;
}

queue <int> q;
int in[N];
bool vis[N];
int ans = 0;

void bfs(){
    while(!q.empty()){
        int now = q.front();
        q.pop();
        ans++;
        for(int i = f[now]; i; i = t[i].next){
            int v = t[i].v;
            in[v]--;
            if(in[v] == 0 && !vis[v]) {
                vis[v] = 1;
                q.push(v);
            }
        }
    } 
    return ;
} 

int main(){
//    freopen("mikado.in", "r", stdin);
//    freopen("mikado.out", "w", stdout);
    int n = read(), m = read();
    for(int i = 1;i <= m; i++){
        int x = read(), y = read();
        in[y]++;
        add(x, y);
    }
    for(int i = 1;i <= n; i++){
        if(in[i] == 0) q.push(i);  // i 不在环内 
    }
    bfs();
    printf("%d
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/wondering-world/p/13376256.html