[HDU2647] Reward(拓扑排序)

传送门:Reward

题目大意是,boss 发工资,每个人都有一个最低工资,而某些员工比如 a 有攀比之心,要求工资必须比其他某个员工比如 b 高,给定若干如 a b 这样的序列,问 boss 最少需要发多少工资。

这就很容易联想到拓扑排序,只不过还需要算出每一层级的点的数量。由于在拓扑排序中,并不一定每个点两两之间都能够有着明确的先后次序,但有些点能够有共同的上级或下级,那么这些点(一个点其实也可以)就能够构成一个层级(以上均为瞎编的词语,明白意思就好,,)。或者说,这些点的集合作为一个整体在图中是有序的,而集合内的点是无法排出次序的。

于是,题目可以转化为:对于一个图,进行拓扑排序,从后到前每个层级的权值依次+1(工资最少导向),计算每个层级的点数乘以该层级的权重的和。

但是,这存在一个问题。如果按照 a b 即 a 与 b 之间连接一条从 a 指向 b 的有向路径,那么进行拓扑排序时就会发现,排在前面的也就是入度为 0 的点(假如是 a),是处于一个无法快速确定它的层级权值的位置。因为 a 要比 b 高,b 可能就要比 c 高,可能一直要到 z 这样老实人才不攀比,但这一切对于刚出队的 a 来说是未知的,也就难以立即确定 a 所在的层级的权重。

为了解决这个问题,我们可以反过来想,把 a b 视为 b 要比 a 低,也就是说,在 a 与 b 之间连接一条从 b 指向 a 的有向路径。那么,这样建图进行拓扑排序,首先出队的就是要求最低的点,而我们已知最低工资的大小,就可以立即确定它的权重了。

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e4+4;
int n, m;
vector<int> g[maxn];
int indeg[maxn];
int t[maxn];

void init()
{
    for(int i = 0; i < maxn; ++i)
        g[i].clear(), indeg[i] = t[i] = 0;
}

void read()
{
    int u, v;
    for(int i = 0; i < m; ++i)
    {
        cin >> u >> v;
        g[v].push_back(u);
        ++indeg[u];
    }
}

bool topoSort()
{
    queue<int> q;
    for(int i = 1; i <= n; ++i)
        if(!indeg[i])
        {
            q.push(i);
            t[i] = 0;
        }

    int cnt = 0;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        ++cnt;

        for(int i = 0; i < g[u].size(); ++i)
        {
            int v = g[u][i];
            if(!(--indeg[v]))
            {
                q.push(v);
                t[v] = t[u]+1;
            }
        }

    }

    if(cnt < n)
        return 0;
    return 1;
}

void solve()
{
    int ans = 0;
    if(topoSort())
    {
        for(int i = 1; i <= n; ++i)
            ans += 888+t[i];
        cout << ans << endl;
    }
    else
        cout << -1 << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    while(cin >> n >> m && n)
    {
        init();
        read();
        solve();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值