传送门:点击打开链接
题意:告诉你n个点,和m条关系,每个关系有u和v,要求u能通过有向边到达v,问要完成这个有向图至少需要多少条边。
思路:就是个xjb的贪心....首先,我们把边按无向图处理,把图分成很多个分块。
对于每个分块,如果这个分块里存在环,那么说明边数>=节点数
对于一个分块,假如我把这些点首尾相连,此时是完美的!因为现在我用了最少的边数,使得可以从一个点到任意一个点,那么无论你给的关系有多么复杂,只要是一个连通图里面的,都是可以做到的。
所以我们就能找出xjb贪心了:
对于一个连通块,如果里面有有向环,那么答案等于点数
如果不存在有向环,答案等于点数-1
然后再xjb搞就行了
#include<map> #include<set> #include<cmath> #include<ctime> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<string> #include<vector> #include<cstring> #include<iomanip> #include<iostream> #include<algorithm> #include<functional> #define fuck(x) cout<<"["<<x<<"]" #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; typedef pair<int, int>PII; const int MX = 2e5 + 5; const int INF = 0x3f3f3f3f; struct Edge { int v, nxt, sign; } E[MX]; int Head[MX], rear; void edge_init() { rear = 0; memset(Head, -1, sizeof(Head)); } void edge_add(int u, int v, int s) { E[rear].v = v; E[rear].nxt = Head[u]; E[rear].sign = s; Head[u] = rear++; } int n, m; int A[MX], cnt[MX], cir[MX], vis[MX], siz; int DFS1(int u, int id) { A[u] = -1; int ret = 1; for(int i = Head[u]; ~i; i = E[i].nxt) { int v = E[i].v; if(!A[v]) ret += DFS1(v, id); } A[u] = id; return ret; } bool DFS2(int u) { vis[u] = -1; bool ret = false; for(int i = Head[u]; ~i; i = E[i].nxt) { int v = E[i].v; if(E[i].sign) { if(vis[v] == -1) { ret = true; break; } if(!vis[v] && DFS2(v)) { ret = true; break; } } } vis[u] = 1; return ret; } int solve() { siz = 0; memset(A, 0, sizeof(A)); memset(cir, 0, sizeof(cir)); memset(cnt, 0, sizeof(cnt)); memset(vis, 0, sizeof(vis)); for(int i = 1; i <= n; i++) { if(!A[i]) siz++, cnt[siz] = DFS1(i, siz); if(!vis[i] && DFS2(i)) cir[A[i]] = 1; } int ans = 0; for(int i = 1; i <= siz; i++) { if(cir[i]) ans += cnt[i]; else ans += cnt[i] - 1; } return ans; } int main() { edge_init(); //FIN; scanf("%d%d", &n, &m); for(int i = 1; i <= m; i++) { int u, v; scanf("%d%d", &u, &v); edge_add(u, v, 1); edge_add(v, u, 0); } printf("%d\n", solve()); return 0; }