Gym

题意:给定一个N点M边的有向图,叫你加最多K条边,使得最小拓扑序最大.

思路:不是那么简单的题.  参照了别人的代码, 最后想通了.

贪心原则: 用两个单调队列维护, 第一个序列S1单增, 表示当前入度为0的点 ; 第二个序列S2单减,表示需要加边的点.

如果S1的最大值大于S2的最大值,则对其加边.

(通俗的说对于当前入度为0的点,  显然就是越小的点越需要加边,  但是我先不给它加边,  而是放到一个集合S2里,   然后遇到最大的点(不能被加边的点)来给S2的点加边.  然后可以想象, 越小的点,  加边得越晚 ,这样可以保证越小的点越被"藏得深".  然后最小拓扑可以达到最大

#include<bits/stdc++.h> 
using namespace std;
const int maxn=100010;
priority_queue<int>p;
priority_queue<int,vector<int>,greater<int> >q;
vector<int>G[maxn];
int cnt,ind[maxn];
int num,used,ans[maxn],a[maxn],b[maxn];
void del(int v){
    ans[++num]=v;
    for(int i=0;i<G[v].size();i++) if(!--ind[G[v][i]]) q.push(G[v][i]);
    if(q.empty()){
        if(!p.empty()){
            int c=p.top(); p.pop();
            G[ans[num]].push_back(c);
            a[++used]=ans[num]; b[used]=c;
            del(c);
        }
    }
}
int main()
{
    int N,M,K,u,v,i,j;
    scanf("%d%d%d",&N,&M,&K);
    for(i=1;i<=M;i++){
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        ind[v]++;
    }
    for(i=1;i<=N;i++) if(!ind[i]) q.push(i);
    while(!q.empty()){
        if(K&&(q.size()>1||(!p.empty()&&q.top()<p.top()))){
            K--;
            int c=q.top(); p.push(c); q.pop();
            if(q.empty()){
                int c=p.top(); p.pop();
                G[ans[num]].push_back(c);
                a[++used]=ans[num]; b[used]=c;
                del(c);
            }    
        }
        else{
            int c=q.top(); q.pop();
            del(c);
        }
    }
    return 0; 
}
原文地址:https://www.cnblogs.com/hua-dong/p/9462418.html