• 【bzoj1742】[Usaco2005 nov]Grazing on the Run 边跑边吃草 区间dp


    题目描述

    John养了一只叫Joseph的奶牛。一次她去放牛,来到一个非常长的一片地,上面有N块地方长了茂盛的草。我们可以认为草地是一个数轴上的一些点。Joseph看到这些草非常兴奋,它想把它们全部吃光。于是它开始左右行走,吃草。John和Joseph开始的时候站在p位置。Joseph的移动速度是一个单位时间一个单位距离。不幸的是,草如果长时间不吃,就会腐败。我们定义一堆草的腐败值是从Joseph开始吃草到吃到这堆草的总时间。Joseph可不想吃太腐败的草,它请John帮它安排一个路线,使得它吃完所有的草后,总腐败值最小。John的数学很烂,她不知道该怎样
    做,你能帮她么?

    输入

    * Line 1 : Two space-separated integers: N and L. N<=1000
    * Lines 2..N+1: Each line contains a single integer giving the position P of a clump (1 <= P <= 1,000,000).

    输出

    * Line 1: A single integer: the minimum total staleness Bessie can achieve while eating all the clumps.

    样例输入

    4 10
    1
    9
    11
    19

    样例输出

    44


    题解

    区间dp,膜拜popoqqq

    因为路过的草一定吃,所以吃的草一定是一段区间。

    用f[i][k]表示吃完从i开始连续的k堆草,且此时在左侧的最小腐败值,

    用g[i][k]表示吃完从i开始连续的k堆草,且此时在右侧的最小腐败值。

    这样我们发现腐败值很难求,并且无法保证最优。

    所以我们可以先计算出每段时间所有草增加的腐败值,这样既能保证dp的成立,又方便计算。

    状态转移方程应该很容易由f/g[i/i+1][k-1]推出来。

    由于空间限制,需要用到滚动数组黑科技。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    long long f[1001][2] , g[1001][2] , p[1001];
    int main()
    {
        int n , i , j , k , cl = 0 , cr = 0;
        long long m;
        scanf("%d%lld" , &n , &m);
        for(i = 1 ; i <= n ; i ++ )
            scanf("%lld" , &p[i]);
        sort(p + 1 , p + n + 1);
        for(i = 1 ; i <= n ; i ++ )
        {
            if(p[i] <= m)
                cl = i;
            if(!cr && p[i] > m)
                cr = i;
        }
        memset(f , 0x3f , sizeof(f));
        memset(g , 0x3f , sizeof(g));
        if(cl) f[cl][1] = g[cl][1] = n * (m - p[cl]);
        if(cr) f[cr][1] = g[cr][1] = n * (p[cr] - m);
        for(k = 2 ; k <= n ; k ++ )
        {
            for(i = 1 ; i + k - 1 <= n ; i ++ )
            {
                j = i + k - 1;
                f[i][k & 1] = min(f[i + 1][~k & 1] + (n - k + 1) * (p[i + 1] - p[i]) , g[i + 1][~k & 1] + (n - k + 1) * (p[j] - p[i]));
                g[i][k & 1] = min(g[i][~k & 1] + (n - k + 1) * (p[j] - p[j - 1]) , f[i][~k & 1] + (n - k + 1) * (p[j] - p[i]));
            }
        }
        printf("%lld
    " , min(f[1][n & 1] , g[1][n & 1]));
        return 0;
    }
  • 相关阅读:
    微信 token ticket jsapi_ticket access_token 获取 getAccessToken get_jsapi_ticket方法
    PHP 日志 记录 函数 支持 数组 对象 新浪 sae 环境 去掉 空格 换行 格式化 输出 数组转字符串
    原生 原始 PHP连接MySQL 代码 参考mysqli pdo
    PHP 数字金额转换成中文大写金额的函数 数字转中文
    使用PHPMailer发送带附件并支持HTML内容的邮件
    设置输出编码格式 header 重定向 执行时间 set_time_limit 错误 报告 级别 error_reporting
    html5 bootstrap pannel table 协议 公告 声明 文书 模板
    指向指针的指针
    二级指针
    c语言:当指针成为参数后
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6396881.html
Copyright © 2020-2023  润新知