题目链接:https://nanti.jisuanke.com/t/19979
题意:给出一个 n 个点,m 条边的 DAG,选出最大的子集使得其中结点两两不能到达。
题解:参考自:https://blog.csdn.net/winter2121/article/details/79849472
首先用弗洛伊德跑出一个可达性矩阵,然后从每个点开始 dfs 跑二分图,则最后 dfs 出的路径为若干条链,而对于链显然除了最后一个结点,其他结点都要删除,而其他结点的总数即跑二分图的匹配数,用总数减去即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define ull unsigned long long 5 #define mst(a,b) memset((a),(b),sizeof(a)) 6 #define mp(a,b) make_pair(a,b) 7 #define pi acos(-1) 8 #define pii pair<int,int> 9 #define pb push_back 10 const int INF = 0x3f3f3f3f; 11 const double eps = 1e-6; 12 const int MAXN = 1e5 + 10; 13 const int MAXM = 2e6 + 10; 14 15 int n,m; 16 int g[110][110],fa[110]; 17 bool vis[110]; 18 19 bool dfs(int u) { 20 for(int i = 1; i <= n; i++) { 21 if(g[u][i] && !vis[i]) { 22 vis[i] = true; 23 if(fa[i] == 0 || dfs(fa[i])) { 24 fa[i] = u; 25 return true; 26 } 27 } 28 } 29 return false; 30 } 31 32 int main() { 33 #ifdef local 34 freopen("data.txt", "r", stdin); 35 // freopen("data.txt", "w", stdout); 36 #endif 37 int t; 38 scanf("%d",&t); 39 while(t--) { 40 scanf("%d%d",&n,&m); 41 for(int i = 1; i <= n; i++) { 42 fa[i] = 0; 43 for(int j = 1; j <= n; j++) g[i][j] = 0; 44 } 45 while(m--) { 46 int u,v; 47 scanf("%d%d",&u,&v); 48 g[u][v] = 1; 49 } 50 for(int k = 1; k <= n; k++) 51 for(int i = 1; i <= n; i++) 52 for(int j = 1; j <= n; j++) 53 g[i][j] |= g[i][k] & g[k][j]; 54 int res = 0; 55 for(int i = 1; i <= n; i++) { 56 mst(vis, 0); 57 if(dfs(i)) res++; 58 } 59 printf("%d\n",n - res); 60 } 61 return 0; 62 }