Problem G. Graph 2015-2016 acmicpc neerc 拓扑排序模拟

来源:互联网 发布:windows字体安装包 编辑:程序博客网 时间:2024/06/01 10:15

一道好题

题目详见题目连接G graph

显然模拟拓扑排序的步骤是必不可少了。

假设我们当前有t个点,他们的入度均为0.我们不知道该选取哪一个。

我们把这t个点按从小到大排好序(放入小顶堆),假设我们目前有k条边(k < t),我们贪心的把前k小的元素都加上一条边,这样的话,我们要选的就是第k+1个点,可以保证这样选取是全局最优的。

现在问题来了,我们给前k个点加一条边,保证了这k个点不被取到,但是,我们怎么知道这条边的父节点是谁呢?没错,我们确实不知道,但是我们目前还不需要知道,所以我们打上标记,可以把他们放入一个大顶堆里面去。大顶堆里面的元素表示入度为1,但父节点尚不清楚的点。


所以每次选取的元素必然与小顶堆及大顶堆有关。

选取时,若小顶堆的大小为size,我们要尽可能挑出大的来,就要使小顶堆里的元素个数尽可能的少,所以我们尽可能用多的边加到小顶堆里的元素上,并把这个元素移动到大顶堆里面去,直到小顶堆里面只剩一个元素。如果边数不够的话,就从小顶堆里面选取最小的元素。

如果边数够的话,小顶堆里还剩一个元素,把这个元素和大顶堆里面最大的元素作比较,如果小顶堆的元素大,那么选取小顶堆里的元素,否则给小顶堆的元素加边,扔到大顶堆里面去,并且从大顶堆里面选取出一个最大的来。


代码:

#include <bits/stdc++.h>using namespace std;const int maxn = 1e5+7;vector<int> G[maxn];int n,m,k;int ind[maxn];set<int> sma,big;typedef set<int>::iterator itp;int cnt;pair<int,int> ans[maxn*10];int main(){freopen("graph.in","r",stdin);      freopen("graph.out","w",stdout);      scanf("%d%d%d",&n,&m,&k);for(int i = 0;i < m;++i) {int u,v;scanf("%d%d",&u,&v);G[u].push_back(v);ind[v]++;}for(int i = 1;i <= n;++i) if(!ind[i]) sma.insert(i);int last = 0;while(sma.size() || big.size()){int rt;itp it;int edges,pre;if(sma.size() == 0){goto mark;}it = sma.begin();edges = min(k,(int)sma.size()-1);pre = 0;for(int i = 0;i < edges;++i){pre = *it;itp nit = it;nit++;sma.erase(it);big.insert(pre);it = nit;}k -= edges;if(k && big.size() && *big.rbegin() > *sma.begin()){k -= 1;big.insert(*sma.begin());sma.clear();mark:rt = *big.rbegin();big.erase(big.find(*big.rbegin()));ans[cnt++] = make_pair(last,rt);}else{rt = *sma.begin();sma.erase(sma.begin());}for(int i = 0;i < G[rt].size();++i){--ind[G[rt][i]];if(ind[G[rt][i]] == 0) sma.insert(G[rt][i]);}printf("%d ",rt);last = rt;}printf("\n%d\n",cnt);for(int i = 0;i < cnt;++i){printf("%d %d\n",ans[i].first,ans[i].second);}return 0;}/*7 4 12 14 34 66 7*/


阅读全文
0 0
原创粉丝点击