贪心的总统
1. 题目要求
★问题描述
水星人的选举大会即将召开,选举大会会在电视上直播,每个竞选者都有一次的发言机会,得票数最高的水星人成为下一届总统。随着时间的推移,观众会越来越少,所以所有的竞选者都想早发言,发言顺序由现任总统决定,所以有些竞选者就想破坏规则,贿赂现任总统,让现任总统一定要让他比他的强力竞争对手更早发言。现任总统全盘接受了贿赂,但他发现要选出一个发言顺序太难了,于是他决定让你帮他找出符合要求的发言顺序。
★数据输入
第一行有一个正整数n,表示有n位竞选者(n个竞选者从1到n编号)参与竞选
接下来有n行,每一行的第一位数 Li 表示第 i 位竞选者的强力竞争对手有 Li 个,接下来有Li个数,分别表示Li个强力竞争对手的编号。
n<=10000, 所有Li的和小于1000000
★数据输出
输出一种发言顺序满足所有贿赂者的要求,若有多种方案,输出字典序最小的一种。
★输入示例
3
0
1 1
2 1 2
★输出示例
3 2 1
2. 代码实现与分析
这道题的本质是靠拓扑排序,要保证字典序最小,所以用优先队列来解决,
第一版的代码使用的是邻接矩阵,空间不够直接爆炸。
所以第二版优化了邻接矩阵,使用bool变量来表示边的关系,空间会比int版的矩阵少上不少,所以过了。
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=10010;
bool f[N][N];
int in_degree[N];
priority_queue<int,vector<int>,greater<int> >q;//优先队列保证字典序最小
const int INF = 0x3f3f3f3f;
void topological_sort_BFS(int n);//拓扑排序
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
memset(f,0, sizeof(f));
memset(in_degree, 0, sizeof(in_degree));
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
for(int j=1;j<=x;j++)
{
int t;
cin>>t;
f[i][t]=1;//边关系建立
in_degree[t]++;//入度+1
}
}
topological_sort_BFS(n);//拓扑排序
return 0;
}
void topological_sort_BFS(int n)
{
for(int i=1;i<=n;i++)
{
if (in_degree[i]==0)
{
q.push(i);
}
}
while(!q.empty())
{
int u=q.top() ;
q.pop();
cout<<u<<" ";
for(int i=1;i<=n;i++)
{
if(f[u][i]==1)
{
in_degree[i]=in_degree[i]-1;//入度-1
if (in_degree[i]==0)
q.push(i);
}
}
}
}
第三版借助某位大佬的启发使用了链式前向星,优化了边的关系表示,空间复杂度降低了不少。
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
const int INF =0x3f3f3f3f;
const int MAX=1e5+5;
int head[MAX];
int cnt=0;
int inDegree[MAX];
priority_queue<int,vector<int>,greater<int> >q;//优先队列保证字典序最小
struct Edge
{
int to,next;
}edge[MAX];
void add_edge(int u,int v)
{
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
int main()
{
memset(head,-1,sizeof(head));
int n,k,m,top;
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>k;
for(int j=0;j<k;++j)
{
cin>>m;
++inDegree[m];
add_edge(i,m);
}
}
for(int i=1;i<=n;++i)
{
if(!inDegree[i])
q.push(i);
}
while(!q.empty())
{
top=q.top();
q.pop();
cout<<top<<" ";
for(int j=head[top];j!=-1;j=edge[j].next)
{
if(!(--inDegree[edge[j].to]))
{
q.push(edge[j].to);
}
}
}
}
// 3
// 0
// 1 1
// 2 1 2
// 3 2 1
3. 总结
拓扑排序有两种实现方式,深度搜索和广度搜索实现,广度搜索比较好理解。