gym 100801G.Graph(拓扑排序,贪心构造)

gym 100801G

题意:

给出 \(n\) 个点 \(m\) 条边的 \(DAG\),要求最多加 \(k\) 条有向边(不能形成环),使得可能的字典序最小的拓扑序列最大。输出最终最小的拓扑序列,以及加边数,加的边(\(1 \leq n \leq 10^5,0 \leq m \leq 10^5\))

题解:

用两个优先队列 \(minQ,maxQ\) ,一个是小元素在前,另一个是大元素在前,前一个维护当前入度为0的所有点,一个用来维护需要被加边的点,然后对于 \(minQ\) 中值最小的点,如果其中还有其他点且k还没用完,那么我们便直接把它扔进 \(maxQ\) 中待处理,这样就是让小的元素晚点拓扑出来;如果第 \(minQ\) 中只有这一个点,我们便考虑能不能从 \(maxQ\) 中找一个最大的点来代替它拓扑出去,然后不断迭代直到两个队列都为空。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1000000007;
const int maxn=1e5+10;
int head[maxn];
struct edge
{
    int to,next;
}e[maxn*2];   //
int tol=0;
void add(int u,int v)
{
    e[++tol].to=v,e[tol].next=head[u],head[u]=tol;
}
priority_queue<int,vector<int>,greater<int> >minQ;
priority_queue<int> maxQ;
int d[maxn];
int ans[maxn];
vector<PII> res;
void release(int u)
{
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(--d[v]==0) minQ.push(v);
    }
}

int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    rep(i,1,m+1)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        d[v]++;
    }
    rep(i,1,n+1) if(!d[i]) minQ.push(i);
    int len=0;
    while(len<n)
    {
        len++;
        while(k&&minQ.size()>1)
        {
            k--;
            int t=minQ.top();
            minQ.pop();
            maxQ.push(t);
        }
        if(minQ.size()==0)
        {
            int t=maxQ.top();
            maxQ.pop();
            ans[len]=t;
            release(t);
            res.pb(make_pair(ans[len-1],t));
        }
        else if(minQ.size()==1&&k&&maxQ.size()&&minQ.top()<maxQ.top())
        {
            k--;
            int t=maxQ.top();
            maxQ.pop();
            maxQ.push(minQ.top());
            minQ.pop();
            ans[len]=t;
            release(t);
            res.pb(make_pair(ans[len-1],t));
        }
        else
        {
            int t=minQ.top();
            minQ.pop();
            ans[len]=t;
            release(t);
        }
    }
    rep(i,1,n+1) printf("%d ",ans[i]);
    puts("");
    printf("%d\n",(int)res.size());
    for(auto it:res)
        printf("%d %d\n",it.fi,it.se);
    return 0;
}

转载于:https://www.cnblogs.com/tarjan/p/7636038.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值