• uva3983


    这是一道思维层层递进的题。

    注意到N最大是100000,C最大是100,所以我们不能设时间复杂度为O(NC)的状态。

    只能设O(N)的状态了。

    设d(i)为从原点出发,将前i个垃圾全部扫完又回到原点的最小代价。(注意这里设的是从原点出发又回到原点,这是为了状态转移的方便而设)

    经典的想法还是把前i个垃圾进行分割。

    d(i) = min{d(j) + dist2origin[j + 1] + dist[j + 1,i] + dist2origin[i] | j <= i, w(j+1,i) <= C}

    搞完前j个的代价 + 从原点移动到j+1的代价 + 从j+1扫到i的代价 + 从i回到原点的代价。条件是在拿着j+1到i这一堆垃圾的时候总总量小于等于C。

    显然直接枚举是一个O(N2)的东西,我们想到dp加速。

    怎么加速,把与j有关的东西抽取出来,每次取当前可以选的j中的最小值。(因为与i有关的值可以O(1)求出)

    这就是利用了单调队列!

    把j相关的量分离出来有:

    d(i) = min(d(j) - total_dist[j+1]+dist2origin(j+1)) + total_dist[i] + dist2origin[i](j同样要满足上面的限制条件)

    如果令func(j) = d(j) - total_dist[j + 1] + dist2origin[j+1]

    则d(i) = min(func(j)) + total_dist[i] + dist2origin[i]

    这个用单调队列搞就好了。

    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 100005;
    
    int t, C, n;
    
    int dist2origin[maxn], total_dist[maxn], total_weight[maxn], f[maxn], q[maxn];
    
    struct node
    {
        int x, y, w;
    }a[maxn];
    
    int func(int i)
    {
        return f[i] - total_dist[i + 1] + dist2origin[i + 1]; 
    }
    
    void solve()
    {
        scanf("%d", &C);
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].w);
            dist2origin[i] = abs(a[i].x) + abs(a[i].y);
            total_dist[i] = total_dist[i - 1] + abs(a[i].x - a[i - 1].x) + abs(a[i].y - a[i - 1].y);
            total_weight[i] = total_weight[i - 1] + a[i].w;
        }
        int front = 1, rear = 1;
        for (int i = 1; i <= n; i++)
        {
            while (front <= rear && total_weight[i] - total_weight[q[front]] > C)//把已经不合适的队头元素清掉。
                front++;
            f[i] = func(q[front]) + total_dist[i] + dist2origin[i];//这样取出来的队头就是func值最小的元素。
            while (front <= rear && func(i) <= func(q[rear]))//保证队列是单调递增的。
                rear--;
            q[++rear] = i;//不要忘了
        }
        printf("%d
    ", f[n]);
        if (t > 0) printf("
    ");
    }
    
    int main()
    {
        scanf("%d", &t);
        while (t--) solve();
        return 0;
    }

    时间复杂度O(N)

  • 相关阅读:
    Oracle 日期总结
    JavaScript 获取文件名,后缀名
    JavaScript Array pop(),shift()函数
    JavaScript Array splice函数
    Oracle 创建表空间、临时表空间、创建用户并指定表空间、授权,删除用户及表空间
    eclipse debug调试java程序的九个技巧
    Oracle dos连接数据库基本操作
    Oracle 隐式游标 存储过程
    Oracle 修改表名
    Oracle 时间 MM-dd形式转换
  • 原文地址:https://www.cnblogs.com/yohanlong/p/7765616.html
Copyright © 2020-2023  润新知