比较综合的一道题目...题意是在一个有向图中给出点的数目和一些点对的可达条件,求最少可以使用多少条边来满足这些条件
比如题目给出一个条件1 2,即1可达2,那么可以用一条边1->2来满足,或者用1->3,3->2两条边来满足但是这样可以新多出两个可达点对即 1 3,3 2
思路:首先如果一个包含n个点无环的连通图,那么至少得需要n-1条边来构造.如果一个包含n个点有环的连通图,那么我们可以用n条边来构造一个首尾相连
的环图,这样所有点之间都可达.至此对于一个包含n个点的连通图而言,所需最少边数不是n-1就是n,取决于该图是否有环
如果给出的不只是一个连通图呢?好,我们可以根据公式k=n-x+y来确定所需的最少边数,其中k是最少需要的边数,x是连通图的数量,y是环的数量
对于一个连通图我们只需要确定是否有环,而不用确定有多少个环,那么y<=x.
至此思路已经明确:1.将所有点划分至各个连通图当中(求连通数) 2.对各个连通分量进行判断是否有环(求环数)
对于1我们可以用dfs来遍历无向图来划分点.
对于2我们得出划分后,对各个连通图进行拓扑排序,可以判断是否有环.
#include <iostream> #include <vector> #include <queue> using namespace std; int indegree[100100]; int outdegree[100100]; int use[100100]; int use2[100100]; vector<int> graph[100100]; vector<int> graph2[100100]; vector<int> G[100100]; int num=0; queue<int> q; void dfs(int v) { for(int i=0;i<graph2[v].size();i++) { if(use[graph2[v].at(i)]==0) { use[graph2[v].at(i)]=1; G[num].push_back(graph2[v].at(i)); dfs(graph2[v].at(i)); } } } int main() { int n,m; cin>>n>>m; for(int i=1;i<=m;i++) { int u,v; cin>>u>>v; graph[u].push_back(v); graph2[u].push_back(v); graph2[v].push_back(u); indegree[v]++; outdegree[u]++; } for(int i=1;i<=n;i++) { if(!use[i]) { num++; use[i]=1; G[num].push_back(i); dfs(i); } } int res2=0; for(int i=1;i<=num;i++) { queue<int> q; for(int j=0;j<G[i].size();j++) { if(indegree[G[i].at(j)]==0) { q.push(G[i].at(j)); } } while(!q.empty()) { int k=q.front(); q.pop(); use2[k]=1; for(int j=0;j<graph[k].size();j++) { indegree[graph[k].at(j)]--; outdegree[k]--; if(indegree[graph[k].at(j)]==0&&!use2[graph[k].at(j)]) { q.push(graph[k].at(j)); } } } for(int j=0;j<G[i].size();j++) { if(use2[G[i].at(j)]==0) { res2++; break; } } } cout<<n-num+res2<<endl; return 0; }
时间: 2024-10-08 19:59:46