UESTC 1558 Charitable Exchange

UESTC_1558

    如果这个题目不考虑t而是改成问最少的转换次数,那么我们只要每次让它转换得越大越好,因此就可以将所有的exchange按r进行排序,每一次在一定的范围内选出v最大的exchange即可(这一点可以用堆来实现)。

    而现在是需要考虑t的,显然就不能直接贪心了,于是我们不妨往dp的角度上想一下。如果dp的话,那么就涉及到一个顺序的问题,按我们上面的思路将r排序后进行dp是否可行呢?是可以的,不妨假设y.r>=x.r且 y能够更新x,而由于y.r>=x.r,那么更新y的那个点就一定也可以更新x,而这样是比用y去更新x更优的。因此,如果我们将exchange按r排序后,后面的点是不会作为更新前面的点的决策的。

    接下来还有一个问题,就是我们如何去选择决策点呢,也就是说对于当前这个点i,我们选择前面的哪个点j来更新i?显然f[j](用到第j个exchange时花费的最小时间)越小越好,但前面拥有最小的f[j]的这个点j是否一定符合要求呢?不一定,因为有可能j.v<i.r。这样我们再联想到选择最小的f[j]是用堆(这里我是用线段树实现的堆)来实现的,那么如果j.v<i.r我们就将其从堆中删除,直到遇到j.v>=i.r的j,这样是否可以呢?其实究竟可不可以是取决于我们当前删除的j还是否可能做为更新后续一些比i大的点的决策,这显然是不会的,因为我们是按r将点排序的,如果j.v<i.r,那么j.v一定小于后续所有点的r。

    这样用线段树+dp的这个算法的可行性就讨论完了,最后再顺便说一下,我一开始写的时候结构体排序使用的是qsort,结果发现一直RE(只剩下了读入数据和排序的部分,其余部分全注释掉了),于是就想到不妨换一下sort试试,从队友那学完sort之后一交竟然真的AC了……难道这个题目对qsort有歧视……

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#define MAXD 100010
#define INF 0x3f3f3f3f3f3f3f3fll
using namespace std;
int N, M, minf[4 * MAXD];
long long f[MAXD];
struct Ex
{
    int v, r, t;
    bool operator < (const Ex &x) const
    {
        return r < x.r;
    }
}ex[MAXD];
void update(int cur)
{
    int ls = cur << 1, rs = cur << 1 | 1;
    minf[cur] = f[minf[ls]] < f[minf[rs]] ? minf[ls] : minf[rs];
}
void build(int cur, int x, int y)
{
    int mid = (x + y) >> 1, ls = cur << 1, rs = cur << 1 | 1;
    minf[cur] = 0;
    if(x == y)
    {
        f[x] = INF;
        return ;
    }
    build(ls, x, mid);
    build(rs, mid + 1, y);
}
void refresh(int cur, int x, int y, int k, int op)
{
    int mid = (x + y) >> 1, ls = cur << 1, rs = cur << 1 | 1;
    if(x == y)
    {
        minf[cur] = (op ? k : 0);
        return ;
    }
    if(k <= mid)
        refresh(ls, x, mid, k, op);
    else
        refresh(rs, mid + 1, y, k, op);
    update(cur);
}
void init()
{
    int i;
    scanf("%d%d", &N, &M);
    ++ N;
    ex[1].v = 1, ex[1].r = ex[1].t = 0;
    for(i = 2; i <= N; i ++)
        scanf("%d%d%d", &ex[i].v, &ex[i].r, &ex[i].t);
    sort(ex + 1, ex + 1 + N);
    build(1, 1, N);
    f[0] = INF, f[1] = 0;
    refresh(1, 1, N, 1, 1);
}
void solve()
{
    int i, j, k;
    long long ans = INF;
    for(i = 2; i <= N; i ++)
    {
        while((k = minf[1]) && ex[k].v < ex[i].r)
            refresh(1, 1, N, k, 0);
        if(k == 0)
            break;
        f[i] = f[k] + ex[i].t;
        refresh(1, 1, N, i, 1);
    }
    for(i = 1; i <= N; i ++)
        if(ex[i].v >= M && f[i] < ans)
            ans = f[i];
    printf("%lld\n", ans == INF ? -1ll : ans);
}
int main()
{
    int t, tt;
    scanf("%d", &t);
    for(tt = 0; tt < t; tt ++)
    {
        init();
        printf("Case #%d: ", tt + 1);
        solve();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/staginner/p/2495619.html