BZOJ 1597 Usaco2008 Mar土地购买

1597: [Usaco2008 Mar]土地购买

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 5515  Solved: 2068
[Submit][Status][Discuss]

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

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

Sample Input

4
100 1
15 15
20 5
1 100
输入解释:
共有4块土地.

Sample Output

500
FJ分3组买这些土地:
第一组:100x1,
第二组1x100,
第三组20x5 和 15x15 plot.
每组的价格分别为100,100,300, 总共500.

HINT

Source

这道题网上的题解让人看得晕乎乎的,这里就来大力写一发

首先这道题肯定是dp无疑

我们发现有很多的状态我们是不需要的

比如当x[i]<x[j]&&y[i]<y[j]的时候,那么i就可以作为j的附属品

只有可能j参与计算而i一定不会参与计算

那么我们就可以把这些状态去掉

变量声明如下

int n;ll f[MAXN];//n为元素个数,f[i]表示枚举到i的时候最小花费 
struct node{
	ll x,y;//x为长,y为宽 
}e[MAXN],ans[MAXN];//e[i]代表原数组,ans[i]代表去重后的数组

内部排序方式如下

inline bool mycmp(node n,node m){
    return n.x==m.x?n.y<m.y:n.x<m.x;
}

代码如下

sort(e+1,e+n+1,mycmp);
int tmp=-1000;
for(int i=n;i>=1;i--){
    if(e[i].y>tmp) tmp=e[i].y;
    else vis[i]=1;
}
int tot=0;
for(int i=1;i<=n;i++){
    if(!vis[i]) ans[++tot].x=e[i].x,ans[tot].y=e[i].y;
}

把ans数组也按上述排序方式进行排序

我们发现ans.y是严格单调递减的  为什么呢?

排序后ans.x单调递增,如果出现ans[i-1].y<ans[i].y的话那么i-1就会成为i的附属品

接下来就很显然了,如果从i到j分为一组的话,面积为ans[i].y*ans[j].x

那么就可以进行dp

直接dp代码如下

for(int i=1;i<=n;i++){
   for(int j=1;j<i;j++){
       f[i]=min(f[i],f[j]+e[j+1].y*e[i].x);
   } 
} 

我们发现这样的时间复杂度是n^2的

我们考虑如何优化

我们发现如果存在k比q有的话

有f[k]+ans[k+1].y*ans[i].x<f[q]+ans[q+1].y*ans[i].x

变形可得

(f[k]-f[q])/(ans[q+1].y-ans[k+1].y)<ans[i].x

所以只要满足这个式子k就比q要优

接下来就比较简单了 维护一个单调递增的队列,队列里存元素的下标

枚举到i的时候如果对头head+1和head满足上面的那个式子的话,就说明对头head没有head+1优,就可以把对头出队,让head+1来当对头,一直进行到不满足上面的那个式子为止

这时候的队头就是最优的,那么f[i]=f[head]+ans[head+1].y*ans[i].x

把计算出来的f[i]拿去用上述的式子去把队尾那些不优的元素去掉,可怎样才算不优的呢

如果i比队尾tail优,队尾tail比tail-1优那么tail的存在就没有任何意义

那么dp转移代码就可以写成如下

head=tail=0;
for(int i=1;i<=tot;i++){
    while(head<tail&&getlow(q[head],q[head+1])<ans[i].x) head++;
    int t=q[head];
    f[i]=f[t]+1LL*ans[t+1].y*ans[i].x;
    while(head<tail&&getlow(q[tail],i<getlow(q[tail1],q[tail])) tail--;
    q[++tail]=i;
}

最后输出f[tot]就可以了

完整的代码

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
inline int read(){
    int x=0;int f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=1e6+10;
int n;ll f[MAXN];//n为元素个数,f[i]表示枚举到i的时候最小花费
struct node{
    ll x,y;//x为长,y为宽 
}e[MAXN],ans[MAXN];//e[i]代表原数组,ans[i]代表去重后的数组 
namespace zhangenming{
    inline bool mycmp(node n,node m){
        return n.x==m.x?n.y<m.y:n.x<m.x;
    }
    inline double getlow(int a,int b){
        return (f[a]-f[b])/(ans[b+1].y-ans[a+1].y);
    }
    ll vis[MAXN]={},q[MAXN],head,tail;
    void init(){
        n=read();
        for(int i=1;i<=n;i++){
           e[i].x=read();
           e[i].y=read();
        }
        sort(e+1,e+n+1,mycmp);
        int tmp=-1000;
        for(int i=n;i>=1;i--){
            if(e[i].y>tmp) tmp=e[i].y;
            else vis[i]=1;
        }
        int tot=0;
        for(int i=1;i<=n;i++){
            if(!vis[i]) ans[++tot].x=e[i].x,ans[tot].y=e[i].y;
        }
        head=tail=0;
        for(int i=1;i<=tot;i++){
            while(head<tail&&getlow(q[head],q[head+1])<ans[i].x) head++;
            int t=q[head];
            f[i]=f[t]+1LL*ans[t+1].y*ans[i].x;
            while(head<tail&&getlow(q[tail],i)<getlow(q[tail-1],q[tail])) tail--;
            q[++tail]=i;
        }cout<<f[tot]<<endl;
    }
}
int main(){
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    using namespace zhangenming;
    init();
    return 0;
}

  

 

posted @ 2017-11-05 21:05  zhangenming  阅读(160)  评论(0编辑  收藏  举报