题意:
给一张有向无环图,可以删掉k条边,要求删掉k条边以后的无环图拓扑排序后字典序最大。
解析:
第一次A出BC的第二题,可惜第一题不能化简(1/1 ->> 1),还是掉分了,发个博客纪念一下。
首先是有向无环图(DAG)的拓扑排序。
拓扑排序意思是先找每个入度为零的点,排在最先,然后将这个点有关边全部删除,更新其他点的入度;
重复上句话,直至图已空,为了保证排序后下标字典序最大,使用优先队列来维护。
然后本题巧在贪心,为了达到字典序最大,删除边的时候,我最先删除的是下标最大的点的入度,然后依次,(若不能删则跳过)。
开始wa的原因是没有考虑到一种情况,就是
5 5 2
1 5
5 2
2 4
4 3
5 4
这样一组数据。
如图:
若按照我的贪心方法,我将删掉边(1->5),(4 -> 3),此时拓扑排序的输出将会是5, 3, 2, 1, 4。
实际上,我们注意到,当5这个点被删掉的时候,它指向4的那条边也被删掉了,所以如果我提前删掉边(2 -> 4),可以达到更好的效果。
所以我提前定义了一个tdu 来代表当前du,删拥有tdu的点就行了。
详见代码。
ps.题解里面用的是线段树维护什么的,不知道是最后减了大数据没超时还是怎么的让我过了,这个算法的时间复杂度是O(nm),主要耗时在度的计算上。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
using namespace std;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double ee = exp(1.0);
vector <int> g[maxn];
int du[maxn];
int tdu[maxn];
int n, m, k;
int L[maxn];
void toposort()
{
memset(du, 0, sizeof(du));
for(int i = 1 ; i <= n; ++i)
{
for(int j = 0 ; j < g[i].size(); ++j)
du[g[i][j]]++;
}
for (int i = 1; i <= n; ++i)
{
tdu[i] = du[i];
}
for (int i = n; i >= 1; --i)
{
if (k <= 0)
{
break;
}
if (tdu[i] <= k)
{
k -= tdu[i];
for(int j = 0 ; j < g[i].size(); ++j)
tdu[g[i][j]]--;
//g[i].clear();
tdu[i] = 0;
du[i] = 0;
}
}
int tot = 0;
priority_queue <int> Q;
for(int i = 1 ; i <= n ; ++i)
if(!du[i])
Q.push(i);
while(!Q.empty())
{
int u = Q.top();
L[tot++] = u;
Q.pop();
for(int j = 0; j < g[u].size(); ++j)
{
int v = g[u][j];
du[v]--;
if(!du[v])
{
Q.push(v);
}
}
}
for (int i = 0; i < tot - 1; i++)
{
printf("%d ", L[i]);
}
printf("%d\n", L[tot - 1]);
return;
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
while (~scanf("%d%d%d", &n, &m, &k))
{
for (int i = 0; i <= n; i++)
{
g[i].clear();
}
for (int i = 1; i <= m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
g[x].push_back(y);
}
toposort();
}
return 0;
}