P2900 [USACO08MAR]Land Acquisition G(斜率优化 dp)

在这里插入图片描述


洛谷链接:洛谷链接

先将土地按 长度 l 排序,相同长度的土地按 宽度 h 排序,若 h i ≤ h j , l i ≤ l j h_i \leq h_j,l_i \leq l_j hihjlilj,可以直接将第 i 块土地和第 j 块土地绑在一起,决策时只需要考虑第 j 块土地。

对排好序的土地维护一个单调栈,栈内土地的 宽度h 单调递减,对最后栈内保留的土地进行 dp

O ( n 2 ) O(n^2) O(n2) 的做法:dp[i] = min(dp[j] + l[i] * h[j + 1])

j > k,在 j 转移比在 k 转移更优,满足 dp[j] + l[i] * h[j + 1] < dp[k] + l[i] * h[k + 1]

移项得到: d p [ j ] − d p [ k ] h [ j + 1 ] − h [ k + 1 ] > − l [ i ] \displaystyle\frac{dp[j] - dp[k]}{h[j + 1] - h[k + 1]}>-l[i] h[j+1]h[k+1]dp[j]dp[k]>l[i]

由于 l[i + 1] >= l[i],对于 dp[i]j点转移 同样比在 k点转移更优,决策具有单调性。

k < j , g ( j , k ) = d p [ j ] − d p [ k ] h [ j + 1 ] − h [ k + 1 ] k < j,g(j,k) =\displaystyle\frac{dp[j] - dp[k]}{h[j + 1] - h[k + 1]} k<j,g(j,k)=h[j+1]h[k+1]dp[j]dp[k]

k < j < i k < j < i k<j<i,若 g ( i , j ) ≥ g ( j , k ) g(i,j) \geq g(j,k) g(i,j)g(j,k),j 点不可能最优决策点,证明:若 g ( j , k ) > − l [ i ] g(j,k) > -l[i] g(j,k)>l[i],则 g ( i , j ) > − l [ i ] g(i,j) > -l[i] g(i,j)>l[i] 一定同时成立, i i i 点比 j j j 点更优;否则 k k k 点比 j j j 点更优

由于这题横坐标单调递减,实际上维护的还是下凸包, i > j i > j i>j,但 x ( i ) < x ( j ) x(i) < x(j) x(i)<x(j),斜率又要单调递增,就是下凸包。

由于决策有单调性(也就是斜率 -l[i] 单调),可以用单调队列维护,在单调队列队头,将 g ( q [ f r o n t ] , q [ f r o n t + 1 ] ) > − l [ i ] g(q[front],q[front + 1]) > -l[i] g(q[front],q[front+1])>l[i] 的点全部 pop,-l[i]越来越小,后面的转移不可能再用到这些点。


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4;
typedef long long ll;
int n;
struct node {
    
    
	int x,y;
	bool operator < (const node &rhs) const {
    
    
		if (x != rhs.x) return x < rhs.x;
		return y < rhs.y;
	}
}a[maxn];
ll h[maxn],l[maxn],top;
ll dp[maxn];
int q[maxn],rear,front;
ll getup(int x,int y) {
    
    
	return dp[x] - dp[y];
}
ll getdown(int x,int y) {
    
    
	return h[x + 1] - h[y + 1];
}
ll calc(int x,int y) {
    
    
	return dp[y] + 1ll * l[x] * h[y + 1];
}
int main() {
    
    
	scanf("%d",&n);
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d%d",&a[i].x,&a[i].y);
	}
	sort(a + 1,a + n + 1);
	for (int i = 1; i <= n; i++) {
    
    
		while (top > 0 && h[top] <= a[i].y) top--;
		h[++top] = a[i].y, l[top] = a[i].x;
	}
	q[++rear] = 0;
	for (int i = 1; i <= top; i++) {
    
    
		while (front + 1 < rear && getup(q[front + 1],q[front + 2]) >= -l[i] * getdown(q[front + 1],q[front + 2])) front++;
		dp[i] = calc(i,q[front + 1]);
		while (front + 1 < rear && getup(i,q[rear]) * getdown(q[rear],q[rear - 1]) >= getup(q[rear],q[rear - 1]) * getdown(i,q[rear]))
			rear--;
		q[++rear] = i;
	}
	printf("%lld\n",dp[top]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/104716820