• P4544 [USACO10NOV]购买饲料Buying Feed


    额,直接思路就dp吧。(我还想了想最短路之类的233但事实证明不行2333.....)

    直入主题:

    化简题意:在x轴上有n个点,坐标为xi。从原点出发,目标点为e,在途中需要收集K重量的物品,在每个点有收集的上限和单价,路费是当前已收集重量*距离,求最小值。

    首先,最暴力的方程式很好推(普及难度),枚举当前点,当前点总重量,nk^2的复杂度(25 0000 0000 233.....)于是直接gg。

    所以,方程式:

    dp[i][j]=min(dp[i-1][p]+dis[i] j^2+w[i-1] (j-p));
    
    枚举一个p, 表示第i-1个商店时有p个货物,那么显然在i-1个商店买了(j-p)个货物,算上在第i-1个商店的花费,加上从i-1到i的路费,就是dp[i][j];

    没错,很暴力(stay sample,stay naive)据zwjdd说裸的能过70分岂不是很赚???

    于是考虑优化.

    1、方程式无法改写,状态无法改变(可能是我太蔡了)

    2、不是斜率优化的形式

    3、没法贪心(废话)

    所以考虑单调队列优化。那我就要找一个无关变量然后把它咕掉喽

    睁大我的小眼,盯着方程式看:有个括号?不爽,展开展开

    dp[i-1][p] - w[i-1]p + dis[i]*j*j + w[i-1]*j;

    所以呢,变量有i(i-1),j,p。

    而i和j都不变,所以对于当前状态来说,影响它的只有p了(装多少)

    开心,把p压到单调队列里变成log应该就能过去了。

    所以开心地来单调队列吧

    考虑两种情况:1、题目限制(装的上限)

    2、最优解(装多少)

    所以,针对第一种情况,如果装完的总量-当前装的量>当前点上限,直接pop掉队头。

    针对第二种情况,也是本题的优化核心。

    在这里提一下单调队列优化的核心,首先要知道单调队列是什么:单调队列是一个内部元素单调增/减的队列(废话)

    那这个性质有什么用呢?它可以大大优化最值的寻找复杂度。优化的方程往往长这样:

    f [ x ] = m a x ( f [ j ] ) + mx
    其中mx是要找的最大值,这也就是网上题解讲的:
    与j的取值无关

    而mx的枚举往往需要一个n的复杂度,所以均摊复杂度就变成了n方。

    单调队列就是一个可以把这个n缩到O(1)的神奇数据结构。(完全不了解的去看滑动窗口)

    在枚举一行(此题的j)中找到一个状态j,确定它是最小值,

    所以,我们针对每一种情况弄出一个p,扔到单调队列里。

    这里,要结合方程式了。队列里的元素单调,我们枚举这些元素,然后代入原方程,比较最值,更新队列顶,维护最值,下面进行更新状态。

    于是:

    if(f[i-1][j]!=0x3f3f3f3f)
    {
        while(!q.empty()&&f[i-1][q.back()]-a[i-1].c*q.back()>=f[i-1][j]-a[i-1].c*j)
        q.pop_back();
        q.push_back(j);
    }

     优化就诞生了,下面只要用队列顶维护状态就可以了。

    #include<bits/stdc++.h>
    using namespace std;
    const long long  maxn=505;
    int K,E,n;
    struct node
    {
        long long  x,f,c;
    }a[maxn];
    bool cmp(node a,node b)
    {
        return a.x<b.x;
    }
    long long  f[maxn][maxn*20];
    long long  dis[maxn];
    int main()
    {
        scanf("%lld%lld%lld",&K,&E,&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld%lld%lld",&a[i].x,&a[i].f,&a[i].c);
        }
        a[++n]=(node){E,0,0};
        sort(a+1,a+n+1,cmp);
        memset(f,0x3f,sizeof(f));
        /*for(long long  i=1;i<=k;i++)
        {
            f[1][i]=a[1]*k;
        }*/
        f[0][0]=0;
        for(int i=1;i<=n;i++)
        {
            deque < long long > q;
            for(int j=0;j<=K;j++)
            {
                while(!q.empty() && j-q.front()>a[i-1].f)
                q.pop_front();
                if(f[i-1][j]!=0x3f3f3f3f)
                {
                    while(!q.empty()&&f[i-1][q.back()]-a[i-1].c*q.back()>=f[i-1][j]-a[i-1].c*j)
                    q.pop_back();
                    q.push_back(j);
                }
                long long  k=q.front();
                if(!q.empty())
                f[i][j]=f[i-1][k]-a[i-1].c*k+(a[i].x-a[i-1].x)*j*j+a[i-1].c*j;
            }
        }
        printf("%lld",f[n][K]);
        return 0;
    }

    (完)

  • 相关阅读:
    Android中Intent传递对象的两种方法(Serializable,Parcelable)
    Android安全机制(2) Android Permission权限控制机制
    Android TextView中文字通过SpannableString来设置超链接、颜色、字体等属性
    finder怎么才能找到library
    Mac下Android Studio中获取SHA1和MD5
    andorid 自定义seekbar
    C# Sending data using GET or POST ZZ
    gmail
    load dll
    C# Read/Write another Process' Memory ZZ
  • 原文地址:https://www.cnblogs.com/ajmddzp/p/11370904.html
Copyright © 2020-2023  润新知