UVALive - 5097 Cross the Wall 斜率DP

Cross the Wall UVALive - 5097
NN 个人,每个人有自己的宽高 wi,hiw_i,h_i ,最多可以挖 KK 个洞,并且不能重叠。挖一个宽高分别为 w,hw,h 的洞需要花费 w×hw\times h 元。当人的宽高 wiw,hihw_i\le w,h_i\le h 时,这个人才能通过这个洞。问至少要花费多少钱才能使所有人都从洞口通过。

首先按照宽高从大到小进行排序,首先按宽,宽相等时再按高来排序。假如存在 w1w2w_1\le w_2 并且 h1h2h_1\le h_2 ,那么就去掉 (w1,h1)(w_1,h_1) 这个点,因为后一个点能过去的话,这个点也一定能过去。这样排序去重之后,对于宽是单调递减的,而对于高是单调递增的(想一想,为什么),因为假如后面的高小于前面的话,由于宽递减,那么这个点会被剔除;假如宽相等,那么后面的高小于等于前面的高,因此这个点也会被剔除。

状态转移方程为:

dp[i][j]=min{dp[k][j1]+w[k+1]×h[i]} dp[i][j]=\min\{dp[k][j-1]+w[k+1]\times h[i]\}

其中 dp[i][j]dp[i][j] 表示前 ii 个人从 jj 个洞钻过去的最小花费,中间枚举 kk ,即前 kk 个人钻 j1j-1 个洞,第 k+1k+1 到第 ii 个人钻一个洞,能够让这部分所有人都钻过去的洞的大小是 w[k+1]×h[i]w[k+1]\times h[i](因为高是递增的,所以第 ii 个高为最大的高)。

k1<k2k_1< k_2 ,若 k2k_2 优于 k1k_1 ,则:

dp[k2][j1]+w[k2+1]h[i]<dp[k1][j1]+w[k1+1]h[i]dp[k2][j1]dp[k1][j1]w[k1+1]w[k2+1]<h[i] \begin{aligned} dp[k_2][j-1]+w[k_2+1]h[i]&<dp[k_1][j-1]+w[k_1+1]h[i]\\ \dfrac{dp[k_2][j-1]-dp[k_1][j-1]}{w[k_1+1]-w[k_2+1]}&<h[i] \end{aligned}

这样就得到了常规的斜率优化的式子。
代码如下:

#include<iostream>
#include<algorithm>
#include<cstring>
//#define WINE
#define MAXN 50010
using namespace std;
typedef long long ll;
int n,k,cnt,h,t,q[MAXN];
ll dp[MAXN][105];
struct Node{
    int w,h;
}a[MAXN];
bool cmp(Node a,Node b){
    if(a.w>b.w)return true;
    if(a.w==b.w&&a.h>b.h)return true;
    return false;
}
ll up(int k2,int k1,int j){
    return dp[k2][j-1]-dp[k1][j-1];
}
ll down(int k2,int k1){
    return a[k1+1].w-a[k2+1].w;
}
ll getDP(int i,int k,int j){
    return dp[k][j-1]+1ll*a[k+1].w*a[i].h;
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    while(scanf("%d%d",&n,&k)!=EOF){
        for(int i=1;i<=n;i++)
            scanf("%d%d",&a[i].w,&a[i].h);
        sort(a+1,a+n+1,cmp);
        cnt=1;
        for(int i=2;i<=n;i++)
            if(a[i].w<a[cnt].w&&a[i].h>a[cnt].h)
                a[++cnt]=a[i];
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=cnt;i++)dp[i][1]=a[1].w*a[i].h;
        k=min(k,cnt);
        for(int j=2;j<=k;j++){
            h=t=0;q[t++]=0;
            for(int i=1;i<=cnt;i++){
                while(h+1<t&&up(q[h+1],q[h],j)<=a[i].h*down(q[h+1],q[h]))
                    h++;
                dp[i][j]=getDP(i,q[h],j);
                while(h+1<t&&up(i,q[t-1],j)*down(q[t-1],q[t-2])<=up(q[t-1],q[t-2],j)*down(i,q[t-1]))
                    t--;
                q[t++]=i;
            }
        }
        printf("%lld\n",dp[cnt][k]);
    }
    return 0;
}

在网站上提交的时候是WA,此时找了好几个其他博客的代码,发现也是WA,然后去 uDebug 上造数据如下:

5 6
100 8
300 7
500 6
600 19
700 30

输出为 5600 ,但是显然是错的,排序去重之后只会剩下 700,30 这个点,所以最小花费应该是 21000 ,或许测评那边出问题了吧…

posted @ 2020-03-12 20:13  winechord  阅读(86)  评论(0编辑  收藏  举报