題意:給定n個點,m條有向邊。
把點分成幾個集合使得每個集合中的任意2點都不可達(一個集合只存放一個點也可以)
問最少需要分成幾個集合。
如果沒有環,則這個題目就是求有向圖的最長鏈,拓撲序下跑bfs即可。
但是有環,所以把環縮點成新點x,而點x的點權就是x點在原圖中對應的頂點個數。
縮點後就是有向無環圖,繼續跑一個拓撲序。
#include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define N 100010 //N為最大點數 #define M 301000 //M為最大邊數 int n, m;//n m 為點數和邊數 struct Edge{ int from, to, nex; bool sign;//是否為橋 }edge[M<<1]; int head[N], edgenum; void add(int u, int v){//邊的起點和終點 Edge E={u, v, head[u], false}; edge[edgenum] = E; head[u] = edgenum++; } int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是點集{u點及以u點為根的子樹} 中(所有反向弧)能指向的(離根最近的祖先v) 的DFN[v]值(即v點時間戳) int taj;//連通分支標號,從1開始 int Belong[N];//Belong[i] 表示i點屬於的連通分支 bool Instack[N]; vector bcc[N]; //標號從1開始 void tarjan(int u ,int fa){ DFN[u] = Low[u] = ++ Time ; Stack[top ++ ] = u ; Instack[u] = 1 ; for (int i = head[u] ; ~i ; i = edge[i].nex ){ int v = edge[i].to ; if(DFN[v] == -1) { tarjan(v , u) ; Low[u] = min(Low[u] ,Low[v]) ; if(DFN[u] < Low[v]) { edge[i].sign = 1;//為割橋 } } else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ; } if(Low[u] == DFN[u]){ int now; taj ++ ; bcc[taj].clear(); do{ now = Stack[-- top] ; Instack[now] = 0 ; Belong [now] = taj ; bcc[taj].push_back(now); }while(now != u) ; } } void tarjan_init(int all){ memset(DFN, -1, sizeof(DFN)); memset(Instack, 0, sizeof(Instack)); top = Time = taj = 0; for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意開始點標!!! } vectorG[N]; int du[N]; void suodian(){ for(int i = 1; i <= taj; i++)G[i].clear(), du[i] = 0; for(int i = 0; i < edgenum; i++){ int u = Belong[edge[i].from], v = Belong[edge[i].to]; if(u!=v)G[u].push_back(v), du[v]++; } } void init(){memset(head, -1, sizeof(head)); edgenum=0;} int dis[N]; int bfs(){ queueq; for(int i = 1; i <= taj; i++) if(du[i]==0){q.push(i); dis[i] = bcc[i].size();} else dis[i] = 0; while(!q.empty()){ int u = q.front(); q.pop(); for(int i = 0; i < G[u].size(); i++){ int v = G[u][i]; dis[v] = max(dis[u]+(int)bcc[v].size(), dis[v]); du[v]--; if(du[v]==0) q.push(v); } } int ans = 1; for(int i = 1; i <= taj; i++)ans = max(ans, dis[i]); return ans; } int main() { int i,j,u,v; while(~scanf("%d %d",&n,&m)){ init(); while(m--){ scanf("%d %d",&u,&v); if(u!=v) add(u,v); } tarjan_init(n); suodian(); printf("%d\n",bfs()); } return 0; } /* 5 5 1 2 2 3 3 4 4 1 5 1 4 4 1 2 2 3 3 4 4 1 5 3 1 2 2 3 3 4 */
淺談程序優化,淺談優化 當初在學校實驗室的時候,常常寫一個
【C++】朝花夕拾——中綴轉後綴,朝花夕拾中綴後綴對於簡單的
為什麼要重載操作符 1. 讓類表現得像
【C++探索之旅】第一部分第十二課:指針一出,誰與爭鋒
[cpp] /* * 程序的版權和版本聲明
BZOJ 2823 AHOI 2012 ÐźÅËþ ͹°