【Week8作业 C】班长竞选【SCC缩点】
題意:
大學班級選班長,n個同學均可以發表意見。若意見為A B,則表示A認為B合適。意見具有傳遞性,即A認為B合適,B認為C合適,則A也認為C合適。共m條意見,要求出最高票數和候選人名單。
思路:
有向圖,有強聯通分量SCC,應先求出所有SCC并縮點。求SCC用kosaraju算法,第一遍dfs1確定原圖的逆后序序列,第二遍dfs2在反圖中按照逆后序序列進行遍歷,每次由起點遍歷到的點即構成一個SCC。
縮點:求完SCC后,此時有scnt個SCC,且c[i]為頂點i所在的SCC編號。結構point里有一個vector,用來存放該SCC里的所有頂點。point P[scnt]就存儲了所有SCC的頂點。用vector G3來存放scc圖,且邊為反向(之后有用)。注意對邊(u,v)添加(c[u],c[v])時,要保證c[u]!=c[v],即兩個點不在同一個SCC里。同時要注意兩個scc頂點間可能會有多條邊,要通過vector的unique函數來去重。
縮點后不難發現對于屬于第i個SCC的點來說,答案分為兩部分,當前SCC中的點除去自己和其他SCC中的點,即ans=SCC[i]-1+sum(SCC[j]),其中j可到達i。稍加思考,可以發現最后答案一定出現在出度為0的SCC中。所以將G3圖中的邊反向,對每個入度為0的點進行dfs3,計算其能到達的點SUM(SCC[j]),即可得到答案。
總結:
一道綜合性很強的題,要求SCC,再縮點,然后dfs,最后答案輸出還要略加注意。還應注意這n個同學的編號為0~n-1。
代碼:
#include <iostream> #include <vector> #include <algorithm> using namespace std; //注意n個同學編號為0~n-1 int n,m,a,b; vector<int> G1[5010],G2[5010]; //原圖與反圖 int c[5010],dfn[5010],vis[5010],dcnt,scnt; //c[i] i號點所在SCC編號;dfn[i] dfs后序列中第i個點 //dcnt dfs序計數;scnt scc計數 void dfs1(int x) {vis[x]=1;for(int i=0; i<G1[x].size(); i++)if(!vis[G1[x][i]])dfs1(G1[x][i]);dfn[dcnt]=x;dcnt++; } void dfs2(int x) {c[x]=scnt;for(int i=0; i<G2[x].size(); i++)if(!c[G2[x][i]])dfs2(G2[x][i]); } void kosaraju() //求SCC {dcnt=0,scnt=0;for(int i=0; i<5010; i++)c[i]=0,vis[i]=0;for(int i=0; i<n; i++)if(!vis[i])dfs1(i);for(int i=n-1; i>=0; i--)if(!c[dfn[i]])++scnt,dfs2(dfn[i]); } //scc頂點編號從1開始 struct point //scc縮點 {vector<int> allV; }; point P[5010]; //用于存放scc的點 vector<int> G3[5010]; //存放scc間的關系,且邊為反向 int indeg[5010]; //入度 int dfs3(int v) //計算入度為0的點的sum(scc[j]) {vis[v]=1;int ans=P[v].allV.size();for(int i=0;i<G3[v].size();i++)if(!vis[G3[v][i]])ans=ans+dfs3(G3[v][i]);return ans; } int main() {ios::sync_with_stdio(false);int T;cin>>T;for(int sjzs=1;sjzs<=T;sjzs++){cin>>n>>m;//圖初始化for(int i=0; i<n; i++)G1[i].clear(),G2[i].clear();//讀入圖 for(int i=0; i<m; i++){cin>>a>>b;G1[a].push_back(b);G2[b].push_back(a);}//求SCCkosaraju();//縮點int sum[scnt+1];for(int i=1;i<=scnt;i++) G3[i].clear(),indeg[i]=0,sum[i]=0,P[i].allV.clear(); for(int i=0;i<n;i++){P[c[i]].allV.push_back(i);for(int j=0;j<G1[i].size();j++){if(c[i]!=c[G1[i][j]]) //兩點不在同一個scc {G3[c[G1[i][j]]].push_back(c[i]);indeg[c[i]]++;}}}//G3圖應去重 for(int i=1;i<=scnt;i++){sort(G3[i].begin(),G3[i].end());G3[i].erase(unique(G3[i].begin(),G3[i].end()),G3[i].end());}for(int i=1;i<=scnt;i++){if(indeg[i]==0){//清空visfor(int j=1;j<=scnt;j++) vis[j]=0;sum[i]=dfs3(i)-1; //自己的點數要-1 }}//輸出int maxpiao=0;for(int i=1;i<=scnt;i++)if(maxpiao<sum[i]) maxpiao=sum[i];int ans[5010],index=0;for(int i=1;i<=scnt;i++){if(sum[i]==maxpiao) //將scci加入到答案序列ans中 {for(int j=0;j<P[i].allV.size();j++){ans[index]=P[i].allV[j];index++;}}}sort(ans,ans+index); cout<<"Case "<<sjzs<<": "<<maxpiao<<endl;for(int i=0;i<index-1;i++)cout<<ans[i]<<" ";cout<<ans[index-1]<<endl;}}總結
以上是生活随笔為你收集整理的【Week8作业 C】班长竞选【SCC缩点】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数据学习笔记之Linux基础(一):L
- 下一篇: OpenCV3.3人脸识别模块的API的