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;
}

浙公网安备 33010602011771号