2019牛客暑期多校训练营(第十场)J(斜率优化dp)

题目链接

题意

n n n 个木材,求制造 k k k 个木板浪费的木材的最小值,木材可以随意组合
制造木板浪费的木材:将 m m m 块木材连在一起,将所有的木材砍成一样的高度,砍掉的就是浪费量

思路

有分治优化,斜率优化, w q s wqs wqs 二分,我写的斜率优化 d p dp dp
先按高度从大到小排序
d p i , j dp_{i,j} dpi,j 表示前 i i i 个木材制成 k k k个木板的最小浪费量
s u m i sum_{i} sumi 表示前 i i i 个木材总面积
s u m w i sumw_{i} sumwi 表示前 i i i 个木材的宽度
d p i , j = d p k , j − 1 + s u m i − s u m k − h i ∗ ( s u m w i − s u m w k ) dp_{i,j} = dp_{k,j-1} + sum_{i} - sum_{k} - h_{i} * (sumw_{i} - sumw_{k}) dpi,j=dpk,j1+sumisumkhi(sumwisumwk)
由于 d p i , j dp_{i,j} dpi,j 只与 d p k , j − 1 dp_{k,j-1} dpk,j1 有关,因此只用记前一个状态就行,设当前状态为 s t a t e state state,则前一个状态为 1 − s t a t e 1 - state 1state
j &lt; k &lt; i j &lt; k &lt; i j<k<i k k k 点比 j j j 点更优,易得
d p k , 1 − s t a t e + s u m i − s u m k − h i ∗ ( s u m w i − s u m w k ) &lt; = d p j , 1 − s t a t e + s u m i − s u m j − h i ∗ ( s u m w i − s u m w j ) dp_{k,1-state} + sum_{i} - sum_{k} - h_{i} * (sumw_{i} - sumw_{k}) &lt;= dp_{j,1-state} + sum_{i} - sum_{j} - h_{i} * (sumw_{i} - sumw_{j}) dpk,1state+sumisumkhi(sumwisumwk)<=dpj,1state+sumisumjhi(sumwisumwj)
d p k , 1 − s t a t e − s u m k + d p j , 1 − s t a t e − s u m j s u m w k − s u m w j &lt; = − h i \frac{dp_{k,1-state} - sum_{k} + dp_{j,1-state} - sum_{j}}{sumw_{k} - sumw_{j}} &lt;= - h_{i} sumwksumwjdpk,1statesumk+dpj,1statesumj<=hi
y i = d p k , 1 − s t a t e − s u m k y_{i} = dp_{k,1-state} - sum_{k} yi=dpk,1statesumk x i = s u m w k x_{i} = sumw_{k} xi=sumwk y k − y j x k − x j &lt; = − h i \frac{y_{k} -y_{j}}{x_{k} - x_{j}} &lt;= - h_{i} xkxjykyj<=hi
说明如果 k k k 为解, j , k , i j,k,i j,k,i 应该是上凸的,则解集是下凸的,用一个单调队列维护解集

#include <cstdio>
#include <cstring>
#include <algorithm>
#define mem(x,y) memset(x,y,sizeof x)
#define fo(i,s,t) for(int i=s;i<=t;i++)
#define cl1(x,y,n) fo(c1_i,0,n-1) x[c1_i]=y
#define cl2(x,y,n,m) fo(c2_i,0,n-1) fo(c2_j,0,m-1) x[c2_i][c2_j]=y
#define inf (~0U>>2)
#define inff (long long)(inf<<1)*(long long)(inf<<1)
using namespace std;
const int maxn=5e3+10;
const int maxm=2e3+10;
const int mod=1e9+7;
int n,k,q[maxn],state=1;
long long sum[maxn],sumw[maxn],dp[maxn][2];
struct node{long long w,h;}a[maxn];
bool cmp(node _x,node _y)
{
    return _x.h>_y.h;
}
///dp[i][j]=dp[k][j-1]+sum[i]-sum[k]-a[i].h*(sumw[i]-sumw[k])
long long x(int i)
{
    return sumw[i];
}
long long y(int i)
{
    return  dp[i][1-state]-sum[i];
}
double slope(int i,int j)
{
    return (y(j)-y(i))*1.0/(x(j)-x(i));
}
int main()
{
    scanf("%d%d",&n,&k);
    fo(i,1,n) scanf("%lld%lld",&a[i].w,&a[i].h);
    sort(a+1,a+n+1,cmp);
    sum[0]=sumw[0]=0;
    fo(i,1,n)
    {
        sum[i]=sum[i-1]+a[i].w*a[i].h;
        sumw[i]=sumw[i-1]+a[i].w;
        dp[i][1]=sum[i]-sumw[i]*a[i].h;
    }
    fo(j,2,k)
    {
        int l=1,r=1;
        q[1]=0;
        state=1-state;
        fo(i,1,n)
        {
            /**不卡精度可以用这个
            while(l<r&&slope(q[l],q[l+1])<=-a[i].h)
                l++;
            dp[i][state]=dp[q[l]][1-state]+sum[i]-sum[q[l]]-a[i].h*(sumw[i]-sumw[q[l]]);
            while(l<r&&slope(q[r-1],q[r])>=slope(q[r],i))
                r--;
            q[++r]=i;*/
            while(l<r&&__int128_t(y(q[l+1])-y(q[l]))<=__int128_t(-a[i].h)*__int128_t(x(q[l+1])-x(q[l])))
                l++;
            dp[i][state]=dp[q[l]][1-state]+sum[i]-sum[q[l]]-a[i].h*(sumw[i]-sumw[q[l]]);
            while(l<r&&__int128_t(y(q[r])-y(q[r-1]))*__int128_t(x(i)-x(q[r]))>=__int128_t(y(i)-y(q[r]))*__int128_t(x(q[r])-x(q[r-1])))
                r--;
            q[++r]=i;
        }
    }
    printf("%lld\n",dp[n][k%2]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值