P7054 [NWRRC 2015] Graph 题解

P7054 [NWRRC 2015] Graph题解

前置知识

拓扑排序,贪心

简要题意

给定一张 \(n\)\(m\) 条边的有向无环图,你可以至多添加 \(k\) 条有向边,使得这仍然是一个有向无环图,使得字典序最小的拓扑序的字典序尽量大。

输出这个拓扑序以及方案。

本题思路

在知道DAG的情况,如何使字典序最小,显然我们用优先队列(小根堆)来维护拓扑序即可。

但是我们可以增加边使图的DAG改变,要使它最大,如果有小根堆的堆顶要取出来,这是我们可以考虑再给它加一条边使它在更后面取出来。这条边的起点是那个点现在我们不知道,但我们可以先把这些往后放的点存起来,答案要最大所以用大根堆。然后我们可以贪心的能往后放就尽可能的往后放。不行就取出存下来的边,如果它是连边被放后的,拓扑序的上一位向它连边一定合法。

具体过程是

  • 小根堆没有点
    • 取出大根堆的头(没有别的选择)
  • 小根堆有一个点
    • 可以连边且大根堆有点且输出大根堆更优,就往后放且取出大根堆(只有一个点不一定要浪费次数)
    • 否则直接取出小根堆
  • 其他
  • 可以向后放就向后放,取出大根堆
  • 否则,取出小根堆

代码

点击查看代码
#include  <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
const int N=1e5+10; 
int n,m,k,in[N],ans[N],flg[N],tot,cnt;
vector<int> ve[N];
priority_queue<int,vector<int>,greater<int> > q;
priority_queue<int,vector<int>,less<int> > p;
pair<int,int> bi[N];
void add(int x){k--;flg[x]=1;p.push(x);} 
void Delete(int x)
{
	ans[++cnt]=x;//加入拓扑序 
	if(flg[x])bi[++tot].fi=ans[cnt-1],bi[tot].se=ans[cnt];//需要加边 
	for(auto i:ve[x]){in[i]--;if(!in[i])q.push(i);}
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++)
	{
		int x,y;cin>>x>>y;
		ve[x].push_back(y);in[y]++;
	}
	for(int i=1;i<=n;i++)if(!in[i])q.push(i);
	while(!q.empty()||!p.empty())
	{
		if(q.empty()) {int u=p.top();p.pop();Delete(u);}//小根堆没有值
		else if(q.size()==1)
		{
			if(k&&!p.empty()&&q.top()<p.top())//可以加边,有点可以做起点且更优 
			{
				int u=q.top();q.pop();add(u);
				u=p.top();p.pop();Delete(u); 
			}
			else {int u=q.top();q.pop();Delete(u);}
		} 
		else
		{
			if(k){int u=q.top();q.pop();add(u);}
			else {int u=q.top();q.pop();Delete(u);}
		} 
	}
	for(int i=1;i<=n;i++)cout<<ans[i]<<' ';
	cout<<'\n'<<tot<<'\n';
	for(int i=1;i<=tot;i++)cout<<bi[i].fi<<' '<<bi[i].se<<'\n';
	return 0; 
}
posted @ 2025-04-04 15:20  exCat  阅读(31)  评论(0)    收藏  举报