J . Wood Processing (斜率优化dp)2019暑假牛客多校 第十场

题目链接:https://ac.nowcoder.com/acm/contest/890/J

题目大意:

        给一群木头,有长和宽,然后一群木头拼起来就是以最低的那个为标准,高于他的全部剪掉,问拼出k块最少浪费多少木头。

题目思路:

       分成k段,每段取最小的价值我们肯定要对木头从低到高进行排序。

       分成k段每段最低的也就是最靠前的那个,全部的木头都要砍成这么低。

       我们可以写一个n*k的dp转移方程。

      dp[i][j] 表示前i块木头分成 j 段最小的浪费值。

          dp[i][j] = min\left \{ dp[j][k-1]+(sum_s[i]-sum[j])-(sum_w[i]-sum_w[j])*h[j+1] \right \}

       sum_s[i]:排好序后,前 i 块木头的面积和。

       sum_w[i]: 排好序后,前 i 块木头的宽度和。

       发现转移方程是n*n*k的需要优化,会发现这个题把方程拆开并移项之后,成为下式。

         dp[j][k-1]-sum_s[j]+sum_w[j]*h[j+1]=sum_w[i]*h[j+1]+(dp[i][k]-sum_s[i])

      sum_w[i] :  作为斜率,满足单调不减性。

      sum[j][k-1]-sum_s[j]+sum_w[j]*h[j+1] : 作为函数值。

      h[j+1]: 作 自变量x

      (dp[i][k]-sum_s[i]) : 作截距

       注意转移的时候dp【i】【1】要自己初始化,不可以直接转移因为dp【i】【0】是非法状态。

        函数值显然具有大于等于0的性质,所以可以使用斜率dp优化处理,从而实现O(1)转移。

        最后复杂度O(n*k)

        极限数据可以发现在斜率交叉相乘的时候会爆ll 所以需要__int128

#include<bits/stdc++.h>
#define ll __int128     
using namespace std;
const ll MAXN = 5005;
ll dp[MAXN][2005];
ll sum_s[MAXN],sum_w[MAXN];
ll que[MAXN];
struct node
{
    long long w,h;
}C[MAXN];
bool cmp(node a,node b)
{
    return a.h<b.h;
}
ll gets(ll i,ll j,ll k)
{
    return (dp[i][k]-sum_s[i]+sum_w[i]*C[i+1].h)-(dp[j][k]-sum_s[j]+sum_w[j]*C[j+1].h);
}
ll getm(ll i,ll j)
{
    return C[i+1].h-C[j+1].h;
}
inline void out(ll x){
    if(x>9) out(x/10);
    putchar(x%10+'0');
}
int main()
{
    int n,k;
    cin>>n>>k;
    for(ll i=1;i<=n;i++){
         cin>>C[i].w>>C[i].h;
    }
    sort(C+1,C+1+n,cmp);
    for(ll i=1;i<=n;i++){
        sum_s[i] = sum_s[i-1]+C[i].w*C[i].h;
        sum_w[i] = sum_w[i-1]+C[i].w;
    }
    for(ll i=1;i<=n;i++){
        dp[i][1]=sum_s[i]-sum_w[i]*C[1].h;
        //cout<<dp[i][1]<<endl;
    }
    for(ll j=2;j<=k;j++){
        ll L = 0,R = -1;
        que[++R] = j-1;
        for(ll i=j;i<=n;i++){
            while(L < R && gets(que[L+1],que[L],j-1) <= sum_w[i]*getm(que[L+1],que[L]) )L++;
            ll now = que[L];
            dp[i][j] = dp[now][j-1] + (sum_s[i]-sum_s[now])-(sum_w[i]-sum_w[now])*C[now+1].h;
            while(L < R && gets(que[R],que[R-1],j-1)*getm(i,que[R]) >= gets(i,que[R],j-1) * getm(que[R],que[R-1]) )R--;
            que[++R]=i;
        }
    }
    out(dp[n][k]);
    puts("");
    //cout<<dp[n][k]<<endl;
}

       

猜你喜欢

转载自blog.csdn.net/qq_41645482/article/details/99704252