BZOJ 1597 Usaco2008 Mar土地购买
1597: [Usaco2008 Mar]土地购买
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 5515 Solved: 2068
[Submit][Status][Discuss]
Description
Input
Output
* 第一行: 最小的可行费用.
Sample Input
100 1
15 15
20 5
1 100
输入解释:
共有4块土地.
Sample Output
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; }