hdu 5195 拓扑排序+贪心

题意:

给一张有向无环图,可以删掉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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值