• D


    D - Pearls

     HDU - 1300 

    这个题目也是一个比较裸的斜率dp,依照之前可以推一下这个公式,这个很好推

    这个注意题目已经按照价格升序排列序,所以还是前缀和还是单调的。

    sum[i] 表示前面 i 种珍珠的花费的前缀和

    dp[i]表示买前面 i 种珍珠需要的最少的花费

    dp[i]=min(dp[j]+(sum[i]-sum[j]+10)*c[i]

    j>k 如果要求选 j  更优,则需要满足下列式子

    dp[j]+(sum[i]-sum[j]+10)*c[i]<dp[k]+(sum[i]-sum[j]+10)*c[i]

    上面式子化简可得 

    dp[j]-dp[k]<c[i]*(sum[j]-sum[k])

    令G[j,k]=(dp[j]-dp[k])/(sum[j]-sum[k])

    如果G[j,k]<c[i]且j>k 则选 j 比选 k 更优

    推出这个斜率的式子之后,然后要用它。

    关键的来了:现在从左到右,还是设k<j<i,如果g[i,j]<g[j,k],那么j点便永远不可能成为最优解,可以直接将它踢出我们的最优解集。为什么呢?

    分三种情况讨论

    1  G[i,j]<G[j,k]<c[t] 那么 i 是比 j 更优的 所以可以排除 j 点

    2 G[i,j]<c[t]<G[j,k]  那么 i 也是比 j 更优的,而且 k 比 j 也优 可以排除 j 点

    3 c[t]<G[i,j]<G[j,k] 那么 k 是比 j 更优 可以排除 j 点

    所以如果是G[i,j]<G[j,k] 且 i>j>k 那么就可以排除掉 j 这个情况,因为 j 两边永远可以找到一个比它更优的,这个也是一种优化

    那如果 G[i,j]>G[j,k]     i>j>k 这种情况呢?

    1 G[i,j]>G[j,k] >c[t] 那么 j  比 i 更优 k 比 j 更优

    2 G[i,j]>c[t]>G[j,k] 那么 j 比 i 更优 j 也比 k 更优

    3 c[t]>G[i,j]>G[j,k] 那么 i 比 j 更优 j 比 k 更优

    这个就可以找到单调性了,

    如果 i 从小往大 G[i,j] 越来越大,那么在 小于等于 c[t] 的范围内,情况也更优,

    意思是 如果 i>j>k 如果可以在 j 点取得最优解,那么k点肯定可以排除,

    所以就是找小于 c[t] 的最大的这个 j 。

    如果是G[i,j]<Gj,k]&&i>j>k 这种情况可以找到一个 j 比两端都优的

    因为这个题目c是单调递增的,所以可以用单调队列来维护。

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #include <string>
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn = 1e3 + 10;
    typedef long long ll;
    ll dp[maxn], sum[maxn], c[maxn];
    int que[maxn];
    
    ll up(int i,int j)
    {
        return dp[i] - dp[j];
    }
    
    ll down(int i,int j)
    {
        return sum[i] - sum[j];
    }
    
    ll DP(int i,int j)
    {
        return dp[j] + (sum[i] - sum[j] + 10)*c[i];
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t--)
        {
            int n;
            scanf("%d", &n);
            sum[0] = dp[0] = 0;
            for (int i = 1; i <= n; i++) scanf("%lld%lld", &sum[i], &c[i]), sum[i] += sum[i - 1];
            int head = 0, tail = 0;
            que[tail++] = 0;
            for(int i=1;i<=n;i++)
            {
                while (head + 1 < tail&&up(que[head + 1], que[head]) <= c[i] * down(que[head + 1], que[head])) head++;//如果满足这个式子
                //就是说明que[head+1]比 que[head]更优,所以可以删去这个que[head] 然后之后不满足这个式子了,说明que[head]比que[head+1]更优
                //说明这个时候的队首就是最优的,就可以跳出循环
                dp[i] = DP(i, que[head]);
                while (head + 1 < tail&&up(i, que[tail - 1])*down(que[tail - 1], que[tail - 2]) <= up(que[tail - 1], que[tail - 2])*down(i, que[tail - 1])) tail--;
                que[tail++] = i;
            }
            printf("%lld
    ", dp[n]);
        }
        return 0;
    }
  • 相关阅读:
    grep如何忽略.svn目录,以及如何忽略多个目录
    CSS写的提示框(兼容火狐IE等各大浏览器)
    校验IPv4和IPv6地址和URL地址
    input框设置onInput事件只能输入数字,能兼容火狐IE9
    $(function(){})、$(document).ready(function(){})....../ ready和onload的区别
    jQuery EasyUI 教程-Tooltip(提示框)
    小知识随手记(一)
    自动换行效果对比
    getComputedStyle与currentStyle获取样式(style/class)
    弹出层框架layer快速使用
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/11397087.html
Copyright © 2020-2023  润新知