Gym - 100801G: Graph (贪心+set+拓扑)(好题)
题意:给定一个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; }
It is your time to fight!