poj2373 灌溉草场

//

动归问题,挺难得。直接粘思路了

从线段的起点向终点安装喷水头,令f(X)表示:所安装喷水头的喷洒范围
恰好覆盖直线上的区间[0 X]时,最少需要多少个喷水头
 显然,X应满足下列条件
 X为偶数
 X所在位置不会出现奶牛,即X不属于任何一个(S,E)
 X2A
 当X2B时,存在Y[X-2B X-2A]且Y满足上述三个条件,使得
f(X)=f(Y)+1

递推计算f(X) 
 f(X) = ∝ : X 是奇数
 f(X) = ∝ : X < 2A
 f(X) = ∝ :X处可能有奶牛出没
 f(X)=1: 2AX2B 、且X位于任何奶牛的活动范围之外
 f(X)=1+min{f(Y): Y[X-2B X-2A]、Y位于任何奶牛的活动范围
之外}: X>2B

f(X)=1+min{f(Y): Y[X-2B X-2A]、Y位于任何奶牛的活动范围之
外}: X>2B
 对每个X求f(X),都要遍历区间 [X-2B, X -2A]去寻找其中最小的
f(Y),则时间复杂度为:L * B = 1000000 * 1000,太慢
 快速找到[X-2B X-2A]中使得f(Y)最小的元素是问题求解速度的关
键 。

可以使用优先队列priority_queue! (multiset也可以,比
priority_queue慢一点)!
 求F(X)时,若坐标属于[X-2B, X-2A]的二元组(i,F(i))都保存在
一个priority_queue中,并根据F(i)值排序,则队头的元素就能
确保是F(i)值最小的。

在求 X点的F(x)时,必须确保队列中包含所有属于 [X-2B,X-2A]的点。
而且,不允许出现坐标大于X-2A的点,因为这样的点对求F(X)无用,如
果这样的点出现在队头,因其对求后续点的F值有用,故不能抛弃之,
于是算法就无法继续了。
 队列中可以出现坐标小于 X-2B 的点。这样的点若出现在队头,则直接
将其抛弃。
 求出X点的F值后,将(X-2A+2, F(X-2A+2))放入队列,为求F(X+2)作准

 队列里只要存坐标为偶数的点即可

/

#include<iostream>

#include<string>
#include<queue>//使用了优先队列
using namespace std;
const int INFINITE = 1 << 31;//无穷大
const int MAXNL = 1000010;
const int MAXNN = 1010;
struct F{ //定义一个struct,储存在优先队列中,并且重载<,使得队首总是ff最小值,方便计算
int x,ff;
bool operator <(const F& a)const{
return ff > a.ff;
}
F(int x_ = 0,int ff_ = 0):x(x_),ff(ff_){};
};
int whetherCow[MAXNL];//数组存放是否有牛
int result[MAXNL];
int N,L,A,B;
priority_queue <F> p;//优先队列
int main(){
cin >> N >> L;
cin >> A >> B;
A *= 2;
B *= 2;//由半径到直径
memset(whetherCow,0,sizeof(whetherCow));//初始化牛数组
for(int i = 0;i < N;i++)
{
int S;
int E;
cin >> S >>E;
whetherCow[S + 1]++;
whetherCow[E]--;  // 起始位置下一个+1,因为是开区间,然后末尾减一,这样通过下面运算能使得有牛的地方都为1
}
int cowNum(0);
for(int i = 0;i <= L;i++)
{
result[i] = INFINITE;//因为偶数,小于2A等数都为infinite,所以在这时候顺便初始化。
cowNum += whetherCow[i];
whetherCow[i] = cowNum > 0;//这样使得有牛的地方都为1
}

for(int i = A ;i <= B;i += 2)//先将A-B的有牛的地方result全部设为A
{
if(!whetherCow[i])
{
result[i] = 1;//从A到B要初始化
if(i <= B - A + 2)//从A到A+2要入队,为什么A+2之后不用
//呢,因为从题目来说,你不能让一个水龙头在(x -A)之间,同时只喷洒一次,所以先只进队A +2,让一会的i从B+2开始循环
p.push(F(i,1));
}
}
for(int i = B + 2;i <= L;i += 2)//从B+2开始循环了
{
if(!whetherCow[i])
{
F f;
while(!p.empty())
{
f = p.top();//取队列首部,判断
if(f.x < i - B)//如果首部x的值比I-B还小,那么他对于判断就没有用了,pop即可
p.pop();
else
break;
}
if(!p.empty())//判断跳出的原因是否为空
{
result[i] = f.ff + 1;//result函数储存当前最小值的个数+1
}
}
//然后i - A+2要入队了
if(result[i - A + 2] != INFINITE) //进队,因为result[i - A +2]已经运算过了,
p.push(F(i - A + 2,result[i - A + 2]));//所以检验一下能否入队,因为下一次就是 i+2了,所以入队一个元素,为什么入队i - A +2呢,
} //因为之前我们保证的是x最大不超过(i-a)(意思),下一个元素就是i-a+2了
if(result[L] == INFINITE)
cout << -1 <<endl;
else
cout << result[L] <<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值