题意:
现在有一面无限大的墙,现在有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\n",ans); 46 } 47 return 0; 48 }
时间: 2024-10-05 06:31:18