[USACO Mar08]土地购买
农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000).
每块土地的价格是它的面积,但FJ可以同时购买多块土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25.
FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.
输入格式:
- 第1行: 一个数: N
- 第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽
样例输入
4 100 1 15 15 20 5 1 100
输入解释:
共有4块土地.
输出格式:
- 第一行: 最小的可行费用.
样例输出
500
输出解释:
FJ分3组买这些土地: 第一组:100x1, 第二组1x100, 第三组20x5 和 15x15 plot. 每组的价格分别为100,100,300, 总共500.
嘛。。斜率优化。。上凸和下凸傻傻分不清楚
那现在我们来看一下这道题= =
首先包含在矩形内部的就不用管它了嘛,总之打包买起来就好了。
那么我们现在得到了x递增而y递减的矩形,如图所示
很容易写出dp方程
f[i]=min(f[j]+y[j+1]*x[i]);
= = 显然(lan),n^2的做法是不能过的
那么斜率优化的公式推法:
假设j的决策比k的决策要好
那么则有f[k]+y[k+1]*x[i]>=f[j]+y[j+1]*x[i]
(y[k+1]-y[j+1])*x[i]>=f[j]-f[k]
那么因为y是单调减少的,所以x[i]的系数一定是整数
则有x[i]>=(f[j]-f[k])/(y[k+1]-y[j+1])
这样就是斜率啦,,
那么下面说一下斜率,拿刚刚的式子举例:
f[i]=f[j]+x[i]*f[j+1]
线性规划的式子可以为 P = x + k * y
至于为什么。。我也不知道为什么x和y一定要定义成酱紫
那么则有:y=-(x/k)+p/k
这样p/k就代表截距,-1/x[i]就是直线的斜率
例如一条斜率为-1/x[i]的直线在一个二维平面上碰点
我们的最优决策一定是下凸壳上的点(使得截距最小的点)
那么根据式子x[i]>=(f[j]-f[k])/(y[k+1]-y[j+1])
如果j满足这个式子,那么k一定木有j要好,这时候就可以把j删掉
而且我们发现x[i]即斜率是递增的
所以即可以维护一个单调队列,保证斜率递增,删除劣决策,队首即为当前最优决策
以下粘代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 50010
using namespace std;
int n;
struct Point{
int x, y;
bool operator<(const Point& k)const{
if(x != k.x)return x < k.x;
return y < k.y;
}
bool operator*(const Point& k)const{
return x <= k.x && y <= k.y;
}
}a[maxn], b[maxn];
long long dp[maxn];
int q[maxn];
double slop(int j, int k){
return (double) (dp[j] - dp[k]) / (b[k + 1].y - b[j + 1].y);
}
int main(){
freopen("acquire.in", "r", stdin);
freopen("acquire.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d%d", &a[i].x, &a[i].y);
int tot = 0;
sort(a + 1, a + 1 + n);
int it = 0;
for(int i = 1; i <= n; i ++){
while(tot && (b[tot] * a[i]))
tot --;
b[++ tot] = a[i];
}
int l = 0, r = 0;
for(int i = 1; i <= tot; i ++){
while(l < r && slop(q[l], q[l + 1]) <= b[i].x)
l ++;
dp[i] = dp[q[l]] + (long long)b[q[l] + 1].y * b[i].x;
while(l < r && slop(q[r - 1], q[r]) > slop(q[r], i))
r --;
q[++ r] = i;
}
printf("%lld\n", dp[tot]);
return 0;
}