• Week8 作业 C


    题目描述:

    大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适,勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?

    输入输出约定及规模:

    本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。

    对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格。

    思路:

    有向图,在一个SCC中,所有的节点都是互相支持的。

    则得票最多的同学所在的SCC一定没有出边(指向其他SCC的边)。

    枚举所有出度为0的SCC,如何找出每一个指向这个SCC的其他SCC?从每个点搜索,看其能否到达这个SCC?

    更好的方法是,用反图,在反图中求这个SCC能达到的其他SCC,票数就是这些SCC中节点的和,然后减去1(自己给自己的票);

    做法:用kosaraju求SCC、在反图中按SCC缩点、枚举入度为0的点、对每个枚举的点搜索

    代码:

    //求SCC时,假设当前联通块编号为i,如果搜到了编号不同的联通块j,就建立i->j的有向边,
    //这样生成的是反图,因为是在反图上求SCC 
    #include <cstdio>
    #include <iostream>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int MAXN=5e3+5;
    bool existEdge[MAXN][MAXN];
    vector<int> G[MAXN],reG[MAXN],cG[MAXN];	//cG=contracted graph 
    int dfn[MAXN],dcnt;			//正图dfs的后序序列 dcnt==dfs后序序列计数
    int SCC[MAXN],numOfSCC,nodesOfSCC[MAXN]; //SCC[i]=k,表示节点i属于第k个联通块 
    int N,M; 
    int visited[MAXN];
    int inDegree[MAXN];
    int ans[MAXN]; 
    void dfs1(int x)
    {
    	visited[x]=1;
    	for(auto y:G[x])
    		if(!visited[y]) dfs1(y);
    	dfn[++dcnt]=x; 
    }
    void dfs2(int x)
    {
    	SCC[x]=numOfSCC;
    	nodesOfSCC[numOfSCC]++;
    	for(auto y:reG[x])
    	{	
    		int u=numOfSCC,v=SCC[y];
    		if( v!=0 && u!=v )	//如果y属于另一个联通块,就建立联通块之间的边
    		{
    		
    			if(existEdge[u][v]==0)
    			{
    				cG[u].push_back(v);
    				existEdge[u][v]=1;
    				inDegree[v]++;	 //反图中,联通块的入度 
    			}
    		}	
    		else if(v==0) dfs2(y);  //如果SCC[y]=0,则y一定是当前SCC的点;否则一定是之前已经搜过的其他SCC的点,这是由Kosaraju算法本身决定的
    	}
    }
    int dfs3(int x)
    {
    	int now=nodesOfSCC[x];
    	visited[x]=1; 
    	for(auto y:cG[x])
    		if(!visited[y]) now+=dfs3(y);
    	return now;
    }
    void kosaraju()
    {
    	dcnt=0,numOfSCC=0;
    	memset(visited,0,sizeof(visited)); 
    	//求正图的后序序列
    	for(int i=0;i<N;i++)
    		if(!visited[i]) dfs1(i);
    	//用逆后序序列搜反图,求联通块
    	for(int i=N;i>=1;i--)
    		if(!SCC[dfn[i]]) numOfSCC++,dfs2(dfn[i]);
    }
    int main()
    {
    //	freopen("a.txt","r",stdin); 
    	int T; cin>>T;
    	for(int K=1;K<=T;K++)
    	{
    		cin>>N>>M;
    		for(int i=0;i<=N;i++)			//注意序号从0开始 
    			G[i].clear(),reG[i].clear(),cG[i].clear();
    		memset(dfn,0,sizeof(dfn));
    		memset(SCC,0,sizeof(SCC));
    		memset(nodesOfSCC,0,sizeof(nodesOfSCC));
    		memset(visited,0,sizeof(visited));
    		memset(inDegree,0,sizeof(inDegree));
    		memset(ans,0,sizeof(ans));
    		memset(existEdge,0,sizeof(existEdge));
    		for(int i=1;i<=M;i++)
    		{
    			int u,v;
    			scanf("%d %d",&u,&v);
    			G[u].push_back(v);
    			reG[v].push_back(u); //反图 
    		}
    		kosaraju();
    		//找反图的缩图中搜入度为0的联通块 
    		vector<int> Nodes;
    		for(int i=1;i<=numOfSCC;i++)
    			if(inDegree[i]==0) Nodes.push_back(i);
    		//在反图的缩图中,从入度为0的联通块开始搜
    		
    		int maxi=-1;
    		for(auto i:Nodes)
    		{
    			memset(visited,0,sizeof(visited)); 
    			ans[i]=dfs3(i)-1;
    			maxi=max(maxi,ans[i]);
    		}
    		vector<int> students;
    		for(int i=1;i<=numOfSCC;i++)
    		{
    			if(ans[i]==maxi)
    			{
    				for(int j=0;j<N;j++)
    					if(SCC[j]==i) students.push_back(j); 
    			}
    		}
    		//输出 
    		printf("Case %d: %d
    ",K,maxi);
    	//	sort(students.begin(),students.end());
    		for(auto x:students)
    			printf( x==students[0] ? "%d" : " %d",x);
    		cout<<endl;
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    ES6、ES7、ES8特性
    【react】XXX项目环境搭建
    map
    vector
    list
    米勒素数模板
    POJ-2421-Constructing Roads(最小生成树 普利姆)
    HDU1301 Jungle Roads(Kruskal)
    Truck History(prime)
    phpstorm快捷键和激活
  • 原文地址:https://www.cnblogs.com/qingoba/p/12720541.html
Copyright © 2020-2023  润新知