大意略。
思路:求得强连通缩点后,可知是DAG图的最小边覆盖,用二分匹配即可。
#include <iostream> #include <cstdlib> #include <cstdio> #include <string> #include <cstring> #include <cmath> #include <vector> #include <queue> #include <algorithm> #include <map> using namespace std; const int maxn = 5010; const int maxm = 100100; struct Edge { int v, next; }edge[maxm], edge2[maxm]; int xlink[maxn], ylink[maxn]; bool vis[maxn]; int first[maxn], first2[maxn]; int stack[maxn], low[maxn], ins[maxn], dfn[maxn]; int belong[maxn]; int cnt, cnt2; int scnt, top, tot; void init() { cnt = cnt2 = 0; scnt = top = tot = 0; memset(first, -1, sizeof(first)); memset(first2, -1, sizeof(first2)); memset(xlink, -1, sizeof(xlink)); memset(ylink, -1, sizeof(ylink)); memset(ins, 0, sizeof(ins)); memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); } int nx, ny; int n, m, k; void read_graph(int u, int v) { edge[cnt].v = v; edge[cnt].next = first[u], first[u] = cnt++; } void read_graph2(int u, int v) { edge2[cnt2].v = v; edge2[cnt2].next = first2[u], first2[u] = cnt2++; } int dx[maxn], dy[maxn]; const int INF = 0x3f3f3f3f; int dis; int bfs() { queue<int> q; dis = INF; memset(dx, -1, sizeof(dx)); memset(dy, -1, sizeof(dy)); for(int i = 1; i <= nx; i++) { if(xlink[i] == -1) { q.push(i); dx[i] = 0; } } while(!q.empty()) { int u = q.front(); q.pop(); if(dx[u] > dis) break; for(int e = first2[u]; e != -1; e = edge2[e].next) { int v = edge2[e].v; if(dy[v] == -1) { dy[v] = dx[u] + 1; if(ylink[v] == -1) dis = dy[v]; else { dx[ylink[v]] = dy[v]+1; q.push(ylink[v]); } } } } return dis != INF; } int find(int u) { for(int e = first2[u]; e != -1; e = edge2[e].next) { int v = edge2[e].v; if(!vis[v] && dy[v] == dx[u]+1) { vis[v] = 1; if(ylink[v] != -1 && dy[v] == dis) continue; if(ylink[v] == -1 || find(ylink[v])) { xlink[u] = v, ylink[v] = u; return 1; } } } return 0; } int MaxMatch() { int ans = 0; while(bfs()) { memset(vis, 0, sizeof(vis)); for(int i = 1; i <= nx; i++) if(xlink[i] == -1) { ans += find(i); } } return ans; } void dfs(int u) { int v; low[u] = dfn[u] = ++tot; stack[top++] = u, ins[u] = 1; for(int e = first[u]; e != -1; e = edge[e].next) { v = edge[e].v; if(!dfn[v]) { dfs(v); low[u] = min(low[u], low[v]); } else if(ins[v]) { low[u] = min(low[u], dfn[v]); } } if(low[u] == dfn[u]) { scnt++; do { v = stack[--top]; belong[v] = scnt; ins[v] = 0; } while(u != v); } } void Tarjan() { for(int v = 1; v <= n; v++) if(!dfn[v]) dfs(v); } inline void readint(int &x) { char c = getchar(); while(!isdigit(c)) c = getchar(); x = 0; while(isdigit(c)) { x = x*10 + c-'0'; c = getchar(); } } inline void writeint(int x) { if(x > 9) writeint(x/10); putchar(x%10+'0'); } void read_case() { init(); readint(n), readint(m); while(m--) { int u, v; readint(u), readint(v); read_graph(u, v); } } void build() { Tarjan(); nx = ny = scnt; for(int u = 1; u <= n; u++) { for(int e = first[u]; e != -1; e = edge[e].next) { int v = edge[e].v; if(belong[u] != belong[v]) read_graph2(belong[u], belong[v]); } } } void solve() { read_case(); build(); int ans = MaxMatch(); writeint(nx-ans), puts(""); } int main() { int T; readint(T); while(T--) { solve(); } return 0; }