標籤:blog http os art 問題 演算法
給定圖,求把至少把圖拆成幾個集合能夠使集合內的點沒有直接或間接關係。
首先由題意可得圖中可能含環,而環裡面的點肯定是要拆開的。
縮點建圖得DAG圖,可以想象一下。。把圖從入度為零的點向下展開,位於同一層的點放在一個集合是沒有關係的,
那麼題目所求的問題就轉化成求圖中最長路的問題了。
這個題的實質和 這題 其實是一模一樣的。。
#include <iostream>#include <cstring>#include <string>#include <cstdio>#include <cmath>#include <algorithm>#include <vector>#include <queue>#include <map>#define inf 0x3f3f3f3f#define eps 1e-6#define ll __int64#define M 100010//圖中點數using namespace std;int sta[M],top; //Tarjan 演算法中的棧bool vis[M]; //檢查是否在棧中int dfn[M]; //深度優先搜尋訪問次序int low[M]; //能追溯到的最早的次序int ccnt; //有向圖強連通分量個數int id; //索引號vector<int> e[M]; //鄰接表表示vector<int> part[M]; //獲得強連通分量結果int inpart[M]; //記錄每個點在第幾號強連通分量裡int degree[M]; //記錄每個強連通分量的度vector<int> edge[M];//縮點後建圖int ans,n,m,dp[M],in[M],point[M];void tarjan(int x){ int i,j; dfn[x]=low[x]=id++; vis[x]=1; sta[++top]=x; for(i=0;i<e[x].size();i++) { j=e[x][i]; if(dfn[j]==-1) { tarjan(j); low[x]=min(low[x],low[j]); } else if(vis[j]) low[x]=min(low[x],dfn[j]); } if(dfn[x]==low[x]) { do { j=sta[top--]; vis[j]=0; part[ccnt].push_back(j); inpart[j]=ccnt; point[ccnt]++; }while(j!=x); ccnt++; }}void solve(int n){ memset(sta,-1,sizeof sta); memset(vis,0,sizeof vis); memset(dfn,-1,sizeof dfn); memset(low,-1,sizeof low); memset(point,0,sizeof point); top=ccnt=id=0; for(int i=1;i<=n;i++) if(dfn[i]==-1) tarjan(i);}int dfs(int x){ if(dp[x]) return dp[x]; dp[x]=point[x]; int i; for(i=0;i<edge[x].size();i++) { int tmp=edge[x][i]; dp[x]=max(dp[x],point[x]+dfs(tmp)); } return dp[x];}int main(){ int n,m,i,j,a,b; while(~scanf("%d%d",&n,&m)) { for(i=0;i<=n;i++) { part[i].clear(); e[i].clear(); edge[i].clear(); } while(m--) { scanf("%d%d",&a,&b); e[a].push_back(b); } solve(n); memset(in,0,sizeof in); for(i=1;i<=n;i++)//枚舉原圖中的邊 { for(j=0;j<e[i].size();j++) { int a=inpart[i]; int b=inpart[e[i][j]];// if(a!=b) { in[b]++; edge[a].push_back(b); } } } ans=0; memset(dp,0,sizeof dp); for(i=0;i<ccnt;i++)//縮點後的圖上是從0到ccnt編號的 { if(!in[i]) ans=max(ans,dfs(i)); } printf("%d\n",ans); } return 0;}