UVALive - 5097 Cross the Wall 斜率DP
Cross the Wall UVALive - 5097
个人,每个人有自己的宽高 ,最多可以挖 个洞,并且不能重叠。挖一个宽高分别为 的洞需要花费 元。当人的宽高 时,这个人才能通过这个洞。问至少要花费多少钱才能使所有人都从洞口通过。
首先按照宽高从大到小进行排序,首先按宽,宽相等时再按高来排序。假如存在 并且 ,那么就去掉 这个点,因为后一个点能过去的话,这个点也一定能过去。这样排序去重之后,对于宽是单调递减的,而对于高是单调递增的(想一想,为什么),因为假如后面的高小于前面的话,由于宽递减,那么这个点会被剔除;假如宽相等,那么后面的高小于等于前面的高,因此这个点也会被剔除。
状态转移方程为:
其中 表示前 个人从 个洞钻过去的最小花费,中间枚举 ,即前 个人钻 个洞,第 到第 个人钻一个洞,能够让这部分所有人都钻过去的洞的大小是 (因为高是递增的,所以第 个高为最大的高)。
设 ,若 优于 ,则:
这样就得到了常规的斜率优化的式子。
代码如下:
#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
,或许测评那边出问题了吧…