P4870 [BalticOI 2009 Day1]甲虫

发布时间 2023-05-01 10:44:36作者: 腾云今天首飞了吗

题意:

有一只甲虫处于一根水平的树枝。因为他沉迷数学无法自拔,所以他觉得很像是在 \(x\) 轴上。
在同一根树枝上,还有 \(n\) 滴露水。每滴露水占用 \(m\) 个单位的水分。相对于甲虫的位置,他们的坐标分别是 \(x_1,x_2,\dots,x_n\)
显然,这一天将会骄阳似火。每过一个时间单位,就会有一个单位的水分从每一滴露水流失。这只甲虫受尽了烈阳的折磨,以至于每当它碰到一滴露水都能瞬间喝完。在每个时间单位中它能移动一个单位的距离。
所以你要写一个程序,根据露水的坐标,计算出甲虫最多能喝到的水。

分析:

与关路灯那一道题相比,这个题恶心的点就在于水是会减少的,但减少到0时就不减少了。类比过去就相当于某个灯的功率减少到0就不变了,因此不能单纯地dp最少的浪费量。

那么,假如抛弃水滴数量减少到0就不变了这个条件,直接嗯dp,怎么才能消除影响呢?
考虑一段水的区间,长度为 \(c\),那么一共就有 \(m * c\) 这么多滴水。总数 - 浪费量就是你所能获得的水的数量。尽管按这个思路dp出的浪费量可能会过大以至于能获得的水的数量为负数,不过因为是取的max,也就无伤大雅。

总的思路如下:

  • 按 P1220 dp出每个区间的最小浪费数。
  • 枚举每个区间,算出最大得到水的数量作为ans
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 300;
int st,n,m,x[MAXN + 5],dp[MAXN + 5][MAXN + 5][2];
bool flag;
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i = 1; i <= n; i++){
        scanf("%lld",&x[i]);
        if(!x[i]){
            flag = 1;
        }
    }
    if(!flag)++n;
    sort(x + 1,x + 1 + n);
    for(int i = 1; i <= n; i++){
        if(x[i] == 0){
            st = i;
            break;
        }
    }
    int ans = 0;
    for(int c = 1; c <= n; c++){
        memset(dp,0x3f,sizeof dp);
        dp[st][st][0] = dp[st][st][1] = 0;
        for(int len = 2; len <= c; len++){
            for(int l = 1; l + len - 1 <= n; l++){
                int r = l + len - 1;
                dp[l][r][1] = min(dp[l][r - 1][1] + (x[r] - x[r - 1]) * (c - len + 1),dp[l][r - 1][0] + (x[r] - x[l]) * (c - len + 1));
                dp[l][r][0] = min(dp[l + 1][r][0] + (x[l + 1] - x[l]) * (c - len + 1),dp[l + 1][r][1] + (x[r] - x[l]) * (c - len + 1));
            }
        }
        for(int i = 1; i + c - 1 <= n; i++){
            ans = max(ans,m * c - dp[i][i + c - 1][0]);   
            ans = max(ans,m * c - dp[i][i + c - 1][1]);
        }
    }
    if(!flag)ans -= m;
    cout << ans;

}

ps:也就多转了个弯,难度就上紫了(