Brexit Negotiations(拓扑排序+反向建图)

题目
题意: 需要开n个会议,一些特定会议开始之前需要先完成别的特定会议,并且在开始之前需要花k分钟总结先前完成的k场会议,每场会议都有原本需要的时长ei。求单场耗时最长的会议的最小值。

不妨设p = ei + k, 想要让 p 尽可能的小, 就要让k尽可能的小的同时, 让ei尽可能的大,也就是说当前若有多个可选择的值时,先选择权值大的.但这样会有一个问题, 这样选只能保证当目前来说是最优解,但是不能保证全局最优,因为我们不知道之后是不是会有比当前更大的ei,比如:

3
1000 1 2
1 0
999 0

若按之前的想法的话,一开始在1和999中选大的,选了999,最大值变成999,然后选1, 更新为1 + 1 = 2, 最后选择1000, 更新为1000 + 2 = 1002.
我们可以发现,这并不是最优解,因为我们可以先选1,然后选1000,得到1000 + 1 = 1,然后再选999,得到999 + 2 = 1001, 最后答案为1001.

那我们不妨想想看, 若从最后选的会议开始往前看,p要怎么样才能最小.其实只要让最后选的ei尽可能小的同时, k尽可能大就可以,也就是先选ei小的.而且逆向来看的话,k是会越来越小的,这样实际上也满足了在ei越大的时候k越小,所以逆向贪心一定是最优解.所以将拓扑排序反向建边之后,每次选ei最小的点更新最大值就可以.

#include <bits/stdc++.h>
#define PII pair<int, int>
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
using namespace std;

const int N = 4e5 + 100;

int h[N], e[N], ne[N], idx, d[N], w[N];
long long res = -1;
int n;

void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx ++;
}

void topsort()
{
    priority_queue<PII,vector<PII>, greater<PII> > Q;
    long long ans = n;
    for(int i = 1; i <= n; i ++)
        if(d[i] == 0) Q.push({w[i], i});
    while(!Q.empty())
    {
        auto head = Q.top();
        Q.pop();
        ans --;  //每次完成的会议数-1
        int now = head.second;
        int v = head.first;
        res = max(res, v + ans);

        for(int j = h[now]; ~j; j = ne[j])
        {
            int to = e[j];
            d[to] --;

            if(d[to] == 0)
                Q.push({w[to], to});
        }
    }
}

int main()
{
    IOS;
    memset(h, -1, sizeof h);
    cin >> n;
    for(int i = 1; i <= n; i ++)
    {
        int x, y;
        cin >> w[i] >> x;
        while(x --)
        {
            cin >> y;
            add(i, y); //反向连边
            d[y] ++;   //记录入度
        }
    }

    topsort();

    cout << res;
    return 0;
}  

/*
6
2 2 4 3
4 1 5
1 2 2 4
3 1 5
2 0
4 1 3
*/
上一篇:查找100-999之间的水仙花数


下一篇:备战金九银十,腾讯技术专家整合2021最全999道Java岗面试题