hdu 3669 Cross the Wall(斜率优化DP)

题目连接:hdu 3669 Cross the Wall

题意:

现在有一面无限大的墙,现在有n个人,每个人都能看成一个矩形,宽是w,高是h,现在这n个人要通过这面墙,现在只能让你挖k个洞,每个洞不能重叠,每个洞需要消耗你挖的w*h,现在问你最小消耗多少。

题解:

设dp[i][j]为前j个人挖i个洞的最小消耗。

首先我们先将每个人按w排序,我们可以发现,排序后的h是递减的,因为如果不是递减的,可以把那个点消掉,因为w[k]<w[j]&&h[k]<h[j]的话,那么第k个人就可以直接过第j个人的洞。

然后我们可以预处理一下。

然后可以得

dp[i][j]=min{dp[i-1][k]+w[j]*h[k+1]}(k<j)

然后考虑斜率优化

设k>l,对于dp[i][j],第k个人到第j个人通过一个洞比第l个人到第j个人通过一个洞更优

有dp[i-1][k]+w[j]*h[k+1]<=dp[i-1][l]+w[j]*h[l+1];

整理得

dp[i-1][k]-dp[i-1][l]/h[l+1]-h[k+1]<=w[j];

然后单调队列优化一下就好。

 1 #include<bits/stdc++.h>
 2 #define F(i,a,b) for(int i=a;i<=b;++i)
 3 using namespace std;
 4 typedef long long ll;
 5 
 6 const int N=5e4+7;
 7 int n,k,Q[N],ed;
 8 ll dp[101][N],ans,inf=1ll<<60;
 9 struct node
10 {
11     ll w,h;
12     bool operator < (const node &b)const{return w<b.w;}
13 }a[N],b[N];
14 
15 ll getx(int k,int l){return -b[k+1].h+b[l+1].h;}
16 ll gety(int i,int k,int l){return dp[i][k]-dp[i][l];}
17 ll check(int i,int j,int k,int l){return gety(i,j,k)*getx(k,l)<=gety(i,k,l)*getx(j,k);}
18 
19 int main()
20 {
21     while(~scanf("%d%d",&n,&k))
22     {
23         F(i,1,n)scanf("%lld%lld",&a[i].w,&a[i].h);
24         sort(a+1,a+1+n),ed=0;
25         F(i,1,n)
26         {
27             while(ed&&a[i].h>b[ed].h)ed--;
28             b[++ed]=a[i];
29         }
30         F(i,1,ed)dp[1][i]=b[i].w*b[1].h;
31         F(i,2,k)
32         {
33             int head=1,tail=0;
34             Q[++tail]=i-1;
35             F(j,i,ed)
36             {
37                 while(head<tail&&gety(i-1,Q[head+1],Q[head])<=b[j].w*getx(Q[head+1],Q[head]))head++;
38                 dp[i][j]=dp[i-1][Q[head]]+b[j].w*b[Q[head]+1].h;
39                 while(head<tail&&check(i-1,j,Q[tail],Q[tail-1]))tail--;
40                 Q[++tail]=j;
41             }
42         }
43         ans=inf;
44         F(i,1,k)if(ans>dp[i][ed])ans=dp[i][ed];
45         printf("%lld
",ans);
46     }
47     return 0;
48 }
View Code
原文地址:https://www.cnblogs.com/bin-gege/p/6152844.html