[USACO08MAR]土地征用Land Acquisition


双倍经验(美滋滋~~):

(1)

(2)


回归正题:

本蒟蒻用的方法:动态规划$+$斜率优化$+$单调队列

其实这种斜率优化的题目大都差不多吧,这里主要讲本题的突破口:

对于每两块土地$x,y$来说,当$l[x]>l[y]$并且$h[x]>h[y]$时,我们可以得到第$y$块土地是没有价值的,因为如果把这两块土地一起购买的话,最长的长选$x$的,而最长的宽也选$x$的

所以我们想到把每块土地按照长度从小到大排序,长度相同按照宽度从小到大排序,把类似上面$y$这种土地的丢弃后(可以用栈来操作),我们会发现一个有趣的现象:

在最优决策下,我们分组分成的土地都会是上面最后的栈内连续的一段,举个例子:

当$l[x]>l[y]>l[z]$并且$h[x]<h[y]<h[z]$时,如果我们不选连续的一段分为一组,即:把$x,z$分为一组,$y$分为一组,那么费用为$l[x]*h[z]+l[y]*h[y]$

而如果我们把这三个连续的分成一组呢?费用就是$l[x]*h[z]$,很明显$y$的费用省去了


考虑$dp$:

设$f[i]$表示前$i$个土地的最小花费

很容易想出$dp$方程:$$f[i]=min(f[i],f[j]+h[j+1]*l[i])$$

然而这时朴素$n^2$的,考虑优化,看到乘法就想到斜率优化了吧。。。。

弄个单调队列维护一下收工咯

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 210007
#define ll long long
using namespace std;
struct node
{
    ll x,y;
}val1[N],val2[N];
ll n;
ll list[N],f[N];
bool cmp(node n1,node n2)
{
    if(n1.x>n2.x) return 0;
    if(n1.x<n2.x) return 1;
    if(n1.y>n2.y) return 0;
    if(n1.y<n2.y) return 1;
    return 1;
}
double Get_k(int x,int y)
{
    return double(f[y]-f[x])/double(val2[x+1].y-val2[y+1].y);
}
signed main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;++i)
        scanf("%lld%lld",&val1[i].x,&val1[i].y);
    sort(val1+1,val1+n+1,cmp);
    int p=1;
	val2[1]=val1[1];
    for(int i=2;i<=n;++i)
    {
        while(p>0&&val2[p].y<=val1[i].y)
			--p;
        val2[++p]=val1[i];
    }
    int head=1,tail=1;list[1]=0;
    for(int i=1;i<=p;++i)
    {
        while(head<tail&&Get_k(list[head],list[head+1])<val2[i].x)
			++head;
        f[i]=f[list[head]]+val2[list[head]+1].y*val2[i].x;
        while(head<tail&&Get_k(list[tail-1],list[tail])>Get_k(list[tail],i))
			--tail;
        list[++tail]=i;
    }
    printf("%lld",f[p]);
}

  

原文地址:https://www.cnblogs.com/yexinqwq/p/10263703.html