C++选强连通分支--班长竞选

题意:

大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?
Input
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。
Output
对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!
Sample Input

2
4 3
3 2
2 0
2 1

3 3
1 0
2 1
0 2

Sample Output

Case 1: 2
0 1
Case 2: 2
0 1 2

思路:

在这里插入图片描述
在这里插入图片描述
具体过程见代码,有详细注释。

代码:

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <queue>
#include  <cstring>
#include <algorithm>
const int inf=0x3f3f3f;
const int maxn=5000+5;
const int maxm=30000+5;
using namespace std;
int N,M,a,b;
struct edge
{//前向星
	int to,next;
} e1[maxm],e2[maxm];//1为原图  2为反图
int head1[maxm],head2[maxm],tot1=0,tot2=0;
void add(int u,int v)
{//加边
	//原图
	e1[++tot1].to=v;
	e1[tot1].next=head1[u];
	head1[u]=tot1;

	//反图
	e2[++tot2].to=u;
	e2[tot2].next=head2[v];
	head2[v]=tot2;
}
int dfn[maxn],vis[maxn],c[maxn],num[maxn],dcnt=0,scnt=0;
void dfs1(int x)
{//dfs1  生成逆后序序列
	vis[x]=1;
	for(int i=head1[x]; i; i=e1[i].next)
		if(!vis[e1[i].to])	dfs1(e1[i].to);
	dfn[++dcnt]=x;
}
void dfs2(int x)
{//强连通分支 c数组为不同的分支做记号  num数组为个分支的元素数
	c[x]=scnt;
	num[scnt]++;
	for(int i=head2[x]; i; i=e2[i].next)
		if(!c[e2[i].to]) dfs2(e2[i].to);
}
void kosaraju()
{//算法
	dcnt=scnt=0;
	memset(c,0,sizeof(c));
	memset(vis,0,sizeof(vis));
	for(int i=1; i<=N; i++)
		if(!vis[i])	dfs1(i);     //逆后序序列
	for(int i=N; i>=1; i--)
		if(!c[dfn[i]])	++scnt,dfs2(dfn[i]);    //强连通分支
}
edge e[maxm];   //缩点后的图
int head[maxm],tot=0,in_deg[maxn],sum[maxn],ans,tmp;  
//in_deg缩点后图的入度 sum为缩点后每个点的票数 
void addd(int u,int v)
{//加缩点后的边
	e[++tot].to=v;
	e[tot].next=head[u];
	head[u]=tot;
}
int dfs(int s)
{
	vis[s]=1;
	tmp+=num[s];    //将缩点后每个点的数量加进去
	for(int i=head[s]; i; i=e[i].next)
	{
		if(!vis[e[i].to])
			dfs(e[i].to);
	}
}
void ini()
{
	memset(head1,0,sizeof(head1));
	memset(head2,0,sizeof(head2));
	memset(head,0,sizeof(head));
	memset(in_deg,0,sizeof(in_deg));
	memset(num,0,sizeof(num));
	memset(sum,0,sizeof(sum));
	memset(c,0,sizeof(c));
	tot1=tot2=tot=0;
}
int main()
{
	int T;
	scanf("%d",&T);
	for(int t=1; t<=T; t++)
	{
		ini();
		scanf("%d %d",&N,&M);
		for(int i=1; i<=M; i++)
		{
			scanf("%d %d",&a,&b);
			add(a+1,b+1);   //读入  整体右移
		}
		kosaraju();  //强连通分支
		for(int i=1; i<=N; i++)
		{//缩点
			for(int j=head1[i]; j; j=e1[j].next)
			{
				if(c[i]!=c[e1[j].to])   //着色不一样 反向建缩点图
				{
					addd(c[e1[j].to],c[i]);
					in_deg[c[i]]++;   //计算入度
				}
			}
		}
		ans=-1;
		for(int i=1; i<=scnt; i++)
		{
			if(in_deg[i]==0)
			{//每个入度为0 的 就是可行答案  按后选择一个最大的		
				tmp=0;
				memset(vis,0,sizeof(vis));
				dfs(i);
				sum[i]=tmp;   //dfs内tmp为缩点后可以到达的所有点的数目和
				ans=max(ans,sum[i]);
			}
		}

		printf("Case %d: %d\n",t,ans-1);
		bool cs = 0;
        for (int i = 1; i <= N; ++i) {
            if (sum[c[i]] == ans) {   //等于ans 在同一个连通分支
                if (cs != 0) {
                    printf(" %d", i - 1);
                }
                else {
                    cs = 1;
                    printf("%d", i - 1);
                }
            }
        }
		printf("\n");
	}
	return 0;
}

总结:

缩点后要反向建图,要不然建出来图什么都不是。。。
前向星可以不要权值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
优秀班委选举系统 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace 优秀班委选举 { public partial class Form1 : Form { private ListBox listBox; public Form1() { InitializeComponent(); } public Form1(ListBox listBox) { InitializeComponent(); this.listBox = listBox; } private void button1_Click(object sender, EventArgs e) { if (listBox1.Items.Count > 0) { if (listBox1.SelectedItem == null) { MessageBox.Show("请先选择一个对象!"); } else { //获取左边listBox1中选中的内容 string cont = listBox1.SelectedItem.ToString(); //左边listBox1中删除选中的内容 listBox1.Items.Remove(cont); //把获得的内容加入到右边的listBox2中 listBox2.Items.Add(cont); } } } private void button2_Click(object sender, EventArgs e) { if (listBox2.Items.Count > 0) { if (listBox2.SelectedItem == null) { MessageBox.Show("请先选择一个对象!"); } else { //获取右边listBox2中选中的内容 string cont = listBox2.SelectedItem.ToString(); //右边listBox2中删除选中的内容 listBox2.Items.Remove(cont); //把获得的内容加入到左边的listBox1中 listBox1.Items.Add(cont); } } } //退出 private void button5_Click(object sender, EventArgs e) { Application.Exit(); } //显示结果 private void button4_Click(object sender, EventArgs e) { //传递listBox2到Form2窗口中 Form2 form2 = new Form2(listBox2); form2.Show(); } //修改名单 private void button3_Click(object sender, EventArgs e) { if (listBox2.SelectedItem == null) { MessageBox.Show("请先选择一个对象!"); } else { string cont = listBox2.SelectedItem.ToString(); int index = listBox2.SelectedIndex; Form3 form3 = new Form3(cont, index, listBox2); form3.Show(); this.Hide(); } } //加载窗口 private void Form1_Load(object sender, EventArgs e) { if (listBox != null) { foreach (string str in listBox.Items) { listBox2.Items.Add(str); } } } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值