4269. 【NOIP2015模拟10.27】挑竹签
题目让我们求最多可以抽走多少个竹签。官方题解是建图然后求拓补排序。
首先,同样的,我们从上面的竹签向被压竹签建有向图。可以发现,不能抽走的竹签都是因为在环中。所以我们统计一下一个点的入度。
对于入度为$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\n", ans); return 0; }