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

original link - https://ac.nowcoder.com/acm/contest/890/J

题意:

给出n个矩形的高和宽,你可以自己排列这些矩形。

现在要将其合并为j堆,每一堆的所有矩形都要砍到这堆的最小值为止,求最少砍掉的面积。

解析:

先从小到大排序,显然最优解一定要先排序。

d p [ i ] [ j ] dp[i][j] dp[i][j]为前 i i i堆合并成 j j j堆的最少砍掉面积, h [ i ] h[i] h[i] i i i的高度, p [ i ] p[i] p[i]为宽度的前缀和, s [ i ] s[i] s[i]为面积的前缀和。

d p [ i ] [ j ] = d p [ y ] [ j − 1 ] + s [ i ] − s [ y ] − h [ y + 1 ] ( p [ i ] − p [ y ] ) dp[i][j]=dp[y][j-1]+s[i]-s[y]-h[y+1](p[i]-p[y]) dp[i][j]=dp[y][j1]+s[i]s[y]h[y+1](p[i]p[y]),令 y > x y>x y>x y y y优于 x x x,有:

d p [ y ] [ j − 1 ] + s [ i ] − s [ y ] − h [ y + 1 ] ( p [ i ] − p [ y ] ) < d p [ x ] [ j − 1 ] + s [ i ] − s [ x ] − h [ x + 1 ] ( p [ i ] − p [ x ] ) dp[y][j-1]+s[i]-s[y]-h[y+1](p[i]-p[y])<dp[x][j-1]+s[i]-s[x]-h[x+1](p[i]-p[x]) dp[y][j1]+s[i]s[y]h[y+1](p[i]p[y])<dp[x][j1]+s[i]s[x]h[x+1](p[i]p[x])
d p [ y ] [ j − 1 ] − d p [ x ] [ j − 1 ] − s [ y ] + s [ x ] − p [ x ] h [ x + 1 ] + p [ y ] h [ y + 1 ] h [ y + 1 ] − h [ x + 1 ] < p [ i ] \dfrac{dp[y][j-1]-dp[x][j-1]-s[y]+s[x]-p[x]h[x+1]+p[y]h[y+1]}{h[y+1]-h[x+1]}<p[i] h[y+1]h[x+1]dp[y][j1]dp[x][j1]s[y]+s[x]p[x]h[x+1]+p[y]h[y+1]<p[i]

- 数据为 1 e 18 1e18 1e18,有的地方不能直接用乘法,要转为 l o n g    d o u b l e long\;double longdouble
- 分母可能为0,得化为乘法
- 我也不知道为什么不加等号会错,反正以后保险起见都加上等号吧。

代码:

#include
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
 
const int maxn=5009;
 
struct node{
    LL w,h;
    bool operator<(const node &R)const{
        return h<R.h;
    }
}e[maxn];
 
LL h[maxn],s[maxn],p[maxn];
 
LL dp[maxn][2009];
bool F1(int j,int x,int y,LL cmp){
    LL lef=dp[y][j-1]-dp[x][j-1]-s[y]+s[x]-p[x]*h[x+1]+p[y]*h[y+1];
    LL rig=cmp;
    rig*=h[y+1]-h[x+1];
    return lef<=rig;
}
 
int Q[maxn],l,r;
 
LL F(int j,int x,int y){
    return dp[y][j-1]-dp[x][j-1]-s[y]+s[x]-p[x]*h[x+1]+p[y]*h[y+1];
}
LL FF(int x,int y){
    return h[y+1]-h[x+1];
}
 
int main(){
    int n,k;scanf("%d%d",&n,&k);
    rep(i,1,n){
        scanf("%lld%lld",&e[i].w,&e[i].h);
    }
    sort(e+1,e+1+n);
    rep(i,1,n){
        h[i]=e[i].h;
        p[i]=p[i-1]+e[i].w;
        s[i]=s[i-1]+e[i].h*e[i].w;
    }
    rep(i,1,n){
        dp[i][1]=s[i]-p[i]*h[1];
    }
    rep(j,2,k){
        Q[l=r=1]=j-1; // dp[j-1][j-1]
        rep(i,j,n){
            while(r>l && F1(j,Q[l],Q[l+1],p[i])) l++;
            int y=Q[l];
            dp[i][j]=dp[y][j-1]+(s[i]-s[y])-(p[i]-p[y])*h[y+1];
 
            while(r>l){
                LL lef=F(j,Q[r-1],Q[r]);
                LL rig=F(j,Q[r],i);
                LL lef__=FF(Q[r-1],Q[r]);
                LL rig__=FF(Q[r],i);
                long double One=1.0;
                if(One*lef*rig__>=One*rig*lef__)r--;
                else break;
            }
            Q[++r]=i;
        }
    }
    printf("%lld\n",dp[n][k]);
}

你可能感兴趣的:(DP动态规划)