您的位置:首页 > 其它

BZOJ 1597 浅谈构造斜率--优化动态规划转移

2017-09-30 16:22 197 查看


世界真的很大

上午考试考得心累

第一题LCA写挂,第三题输出少了个感叹号???“!”

然后想下午趁头脑清晰做一道DP,然后一个班小时就这么过去了

今天运气真的是。。

复习一波斜率优化,原来学过但是感觉忘得差不多了

老老实实把方程写在纸上,一步一步写,不然真的要把自己搞蒙

看题先:

description:

农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.

input:

第1行: 一个数: N

第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽

output:

第一行: 最小的可行费用.

拿到这道题,感觉是DP,因为还要求分段什么的

很自然想到f[i]表示前i个土地,分成“管他多少”段并且i为最后一段末尾的最优值

那么问题就在于,土地并不是序列一样的东西,不存在”前i个土地“什么的,不能把其给出的土地顺序直接就来DP,因为这样等于分的某一段的一定是给出的顺序的连续的某一段,想想就觉得不可能

然后想一想,某一段土地的权值,是其最大的横坐标,和最大的纵坐标之积,那么在一段里面,单论横坐标应该是连续的一段,为了使得每一个x坐标覆盖的范围尽量多。

然后得出结论,这个”序列“应该和x序有一定关系,然后就把样例按x排了个序,所有矩形的左下角靠着原点

画出来,发现一个神奇的东西,”在x单调上升的同时,y单调下降“

当时就一阵蒙蔽,这难道是必然的吗

马上画了个反例。。

但是一旦出现反例,发现这个反例的矩形的x和y都被另一个更大的矩形包括,这就使得一旦选了这个更大的矩形,小矩形就完全不用考虑了,随便放进去就行

然后就把这样的矩形删掉,赫然发现:”在x单调上升的同时,y单调下降“,这就变成了一个必然的规律了

这样”序列“就出来了,我们分的所有段,必然是这些矩形里面连续的一段

然后就可以一下子写出DP方程:

f[i]=min(f[j]+a[j+1].y * a[i].x)

但是这样是n^2的,所以考虑优化

方程转移时填表法,一开始想能不能什么数据结构,但是由于右边有一个a[i].x,所以没办法用什么数据结构维护起来,因为每次选最值的条件都不一样

在这样的情况下,且与i(当前枚举项)相关的项同时出现在了方程两边,很自然,斜率优化。

设j,k,设j作为转移项比k更优,那么:

f[j]+a[j+1].y * a[i].x < f[k]+a[k+1] .y * a[i].x

然后由于i与j和k无关,所以把i挪到一边,得:

(f[j]-f[k]) / (a[k+1] .y * a[i].x - a[j+1].y * a[i].x)

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long dnt;

struct land
{
dnt x,y;
}lnd[500010],a[500010];

int n,tot=0,q[2000010],h,t;
dnt f[500010];

bool cmp(const land &a,const land &b)
{
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}

bool check_h(int i)
{
return f[q[h+1]]-f[q[h]] < (a[q[h]+1].y-a[q[h+1]+1].y) * a[i].x;
}

bool check_t(int i)
{
return (f[q[t]]-f[q[t-1]])*(a[q[t]+1].y-a[i+1].y) > (f[i]-f[q[t]])*(a[q[t-1]+1].y-a[q[t]+1].y);
}

int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
cin >> lnd[i].x >> lnd[i].y;
sort(lnd+1,lnd+n+1,cmp);
for(int i=1;i<=n;i++)
{
while(tot && lnd[i].y>=a[tot].y) tot--;
a[++tot]=lnd[i];
}
h=1,t=1,q[1]=0;
for(int i=1;i<=tot;i++)
{
while(h<t && check_h(i)) h++;
f[i]=f[q[h]]+a[q[h]+1].y*a[i].x;
while(h<t && check_t(i)) t--;
q[++t]=i;

}
cout << f[tot] << endl;
return 0;
}
/*
EL PSY CONGROO
*/


嗯,就是这样
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: