• [Baltic2009]beetle【区间Dp】


    Online Judge:Bzoj1761

    Label:区间Dp

    题目描述

    在一条直线上有N个点,每个点M升水. 一个虫子在坐标轴0点上,它每个单位时间移动一格,每个点的水每单位时间消失1升. 问虫子最多可以喝到多少水,喝水的时间忽略不计。

    输入

    第一行给出数字N,M

    下面N行给出N个点的坐标Xi

    (0≤n≤300), (1≤m≤1,000,000),$ −10,000 ≤ x1, x2, . . . , xn ≤ 10,000$

    输出

    最多可以喝到多少水。

    样例

    Input

    3 15
    6
    -3
    1
    

    Output

    25
    

    Hint

    虫子开始在0点,它先到1这个点喝水,再到-3,再到6.

    题解

    类似但更为简单的一道题:USACO2005 nov [Grazing on the Run]

    本题题解将基于上面这道的题解来bb,没做过可以先去看这题。

    上面那个问题其实求的是:

    你的初始坐标为x,你要去数轴上的n个点,每个点原有无限水量。每秒每个点都会少掉一单位水,问遍历完所有点,浪费的最少水量是多少?

    而现在我们求的是最多能喝到多少水,而且不一定需要遍历所有点(因为可能你到那了水已经没了,等同于没去遍历)。

    假设我们上面那道题求出结果为(f[1][n]),难道这道题答案就是(M*N-f[1][n])吗?看起来好像没有问题,似乎是总的水量-浪费的水量,但是,我们上面求出的dp值计算的是所有点的浪费总水量和,但本题中如果我们只考虑去区间([l,r](l!=1||r!=n))则浪费水量会多计了,答案自然不正确。

    如何避免这个问题呢?观察数据范围,上面那题(n<=1000),而这题(n<=300),似乎暗示我们可以再枚举一维:)

    我们现在不知道最终走过的区间长度,那就去枚举它,这样我们就可以确定水的总量,从而可以确定每秒浪费的水量(不在我这个区间内的点我就不管它了)。剩下的部分就跟上面那道题一模一样了。

    综上,时间复杂度为(O(N^3))

    完整代码如下

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=305,INF=0x3f3f3f3f;
    int a[N],dis[N][N],ans,f[N][N][2];
    int n,m;
    inline void Do(int &x,int y){(x>y)&&(x=y);}
    signed main(){
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    	a[++n]=0;
    	sort(a+1,a+n+1);
    	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)dis[i][j]=abs(a[i]-a[j]);
    	int st=lower_bound(a+1,a+n+1,0)-a;	
    	for(int tot=1;tot<=n;tot++){//枚举最终走过区间长度
    		memset(f,0x3f,sizeof(f));
    		f[st][st][0]=f[st][st][1]=0;//注意每次都要初始化
    		for(int i=1;i<=tot;i++)for(int l=1;l+i-1<=n;l++){
    			int r=l+i-1;
    			if(f[l][r][0]<INF){
    				if(l!=1)Do(f[l-1][r][0],f[l][r][0]+dis[l-1][l]*(tot-i));
    				if(r!=n)Do(f[l][r+1][1],f[l][r][0]+dis[l][r+1]*(tot-i));
    			}	
    			if(f[l][r][1]<INF){
    				if(l!=1)Do(f[l-1][r][0],f[l][r][1]+dis[r][l-1]*(tot-i));
    				if(r!=n)Do(f[l][r+1][1],f[l][r][1]+dis[r][r+1]*(tot-i));	
    			}
    		}
    		for(int i=1;i+tot-1<=n;i++){//此时喝到水量=(区间总水量-区间浪费水量)	
    			ans=max(ans,(tot-1)*m-min(f[i][i+tot-1][0],f[i][i+tot-1][1]));
    		}
    	}
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    C#代码动态编译、动态执行、动态调试
    php’s explode() 函数
    exec函数族
    在C语言中执行shell命令
    dmesg简介
    错误输出
    php’s json_decode函数
    使expect脚本传回返回值
    php’s fgets() 函数
    统计当前目录下的所有文件目录大小,不显示子目录与子文件
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11637361.html
Copyright © 2020-2023  润新知