Gym - 100801G: Graph (贪心+set+拓扑)(好题)

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

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

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

对于第一个集合, 显然小的点更需要加边,所以在K还有剩余的时候,把S1的最小值加入S2, 如果K没有剩余. 那么从小到大输出.

而S1和S2的关系是 : 当S1的最大值给向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; 
}

猜你喜欢

转载自www.cnblogs.com/hua-dong/p/9462418.html