[USACO08FEB]酒店Hotel

题目描述

The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Street as their vacation residence. This immense hotel has N (1 ≤ N ≤ 50,000) rooms all located on the same side of an extremely long hallway (all the better to see the lake, of course).

The cows and other visitors arrive in groups of size Di (1 ≤ Di ≤ N) and approach the front desk to check in. Each group i requests a set of Di contiguous rooms from Canmuu, the moose staffing the counter. He assigns them some set of consecutive room numbers r..r+Di-1 if they are available or, if no contiguous set of rooms is available, politely suggests alternate lodging. Canmuu always chooses the value of r to be the smallest possible.

Visitors also depart the hotel from groups of contiguous rooms. Checkout i has the parameters Xi and Di which specify the vacating of rooms Xi ..Xi +Di-1 (1 ≤ Xi ≤ N-Di+1). Some (or all) of those rooms might be empty before the checkout.

Your job is to assist Canmuu by processing M (1 ≤ M < 50,000) checkin/checkout requests. The hotel is initially unoccupied.

第一行输入n,m ,n代表有n个房间,编号为1—n,开始都为空房,m表示以下有m行操作,以下 每行先输入一个数 i ,表示一种操作:

若i为1,表示查询房间,再输入一个数x,表示在1–n 房间中找到长度为x的连续空房,输出连续x个房间中左端的房间号,尽量让这个房间号最小,若找不到长度为x的连续空房,输出0。

若i为2,表示退房,再输入两个数 x,y 代表 房间号 x—x+y-1 退房,即让房间为空。

输入输出格式

输入格式:
Line 1: Two space-separated integers: N and M

Lines 2..M+1: Line i+1 contains request expressed as one of two possible formats: (a) Two space separated integers representing a check-in request: 1 and Di (b) Three space-separated integers representing a check-out: 2, Xi, and Di
输出格式:
Lines 1…..: For each check-in request, output a single line with a single integer r, the first room in the contiguous sequence of rooms to be occupied. If the request cannot be satisfied, output 0.
输入输出样例

输入样例#1:
10 6
1 3
1 3
1 3
1 3
2 5 5
1 6
输出样例#1:
1
4
7
0
5
说的夸张一点,我两年才做会这一题(orz) 之前做的是小白逛公园
思路理解之后其实很清晰:线段树维护三个最大值
1.这个区间内最多的连续空房间的个数
2.这个区间从左端点开始向右的空房间的个数
3.这个区间从友端点开始向左的空房间的个数

而 1 又可以从子树的1,2 , 3过来
mmax 为1
lmax 为2
rmax 为3

因此有 f[x].max:=max{ max[ max(f[x*2].mmax,f[x*2+1].mmax),f[x*2].rmax+f[x*2+1].l) ] ,max(f[x].lmax,f[x].rmax) } (有点多自己理解下,不难)
如果左子树房间都是空的 则 f[x].lmax:=f[x*2].mmax+f[x*2+1].lmax
否则 f[x].lmax:=f[x*2].lmax
如果右子树房间都是空的 则 f[x].rmax:=f[x*2+1].mmax+f[x*2].rmax
否则 f[x].rmax:=f[x*2+1].rmax

其余的具体分析一下即可
代码就不解释了(话说学p的已经快绝迹了)

这里写代码片program df;
type point=record
a,b,c,d,lm,rm:longint;
end;


var i,j,n,m,x,y,z,k,t:longint;
f:array[0..500000] of point;
ff:boolean;
function max(x,y:longint):longint;
begin
if x>y then exit(x)
else exit(y);
end;

procedure built(z,x,y:longint);
var mid:longint;
begin
f[z].a:=x;
f[z].b:=y;
if x=y then
begin
f[z].c:=1;
f[z].lm:=1;
f[z].rm:=1;
exit;
end;
mid:=(x+y) div 2;
built(z*2,x,mid);
built(z*2+1,mid+1,y);
f[z].c:=max(max(f[z*2].c,f[z*2+1].c),f[z*2].rm+f[z*2+1].lm);
f[z].c:=max(max(f[z].c,f[z].lm),f[z].rm);
if   f[z*2].c=f[z*2].b-f[z*2].a+1                then f[z].lm:=f[z*2].c+f[z*2+1].lm    else    f[z].lm:=f[z*2].lm;

if   f[z*2+1].c=f[z*2+1].b-f[z*2+1].a+1    then f[z].rm:=f[z*2+1].c+f[z*2].rm    else    f[z].rm:=f[z*2+1].rm;

//writeln(x,' ',y,' ',f[z].c,' ',f[z].lm,' ',f[z].rm);
end;

procedure check(z:longint);
var ans:longint;
begin
//if (f[x*2].c=(f[x*2].b-f[x*2].a)) then
if   f[z*2].c=f[z*2].b-f[z*2].a+1                then f[z].lm:=f[z*2].c+f[z*2+1].lm    else    f[z].lm:=f[z*2].lm;
if   f[z*2+1].c=f[z*2+1].b-f[z*2+1].a+1    then f[z].rm:=f[z*2+1].c+f[z*2].rm    else    f[z].rm:=f[z*2+1].rm;
f[z].c:=max(max(f[z*2].c,f[z*2+1].c),f[z*2].rm+f[z*2+1].lm);
f[z].c:=max(max(f[z].lm,f[z].rm),f[z].c);
//writeln(f[z].a,' ',f[z].b,' ',f[z].c,' ',f[z].lm,' ',f[z].rm);

end;



procedure xiugai(x:longint);
var l,r:longint;
begin
if f[x].d=0 then exit;
if f[x].d=-1 then
begin
f[x*2].c:=0;
f[x*2].lm:=0;
f[x*2].rm:=0;
f[x*2+1].c:=0;
f[x*2+1].lm:=0;
f[x*2+1].rm:=0;
f[x*2].d:=-1; 
f[x*2+1].d:=-1;
end
else
begin
l:=f[x*2].a; r:=f[x*2].b;
f[x*2].c:=r-l+1;
f[x*2].lm:=r-l+1;
f[x*2].rm:=r-l+1;
l:=f[x*2+1].a; r:=f[x*2+1].b;
f[x*2+1].c:=r-l+1;
f[x*2+1].lm:=r-l+1;
f[x*2+1].rm:=r-l+1;
f[x*2].d:=1; 
f[x*2+1].d:=1;
end;
f[x].d:=0;
end;


procedure search(x,y:longint);
var mid:longint;
begin
xiugai(x);
if f[x].a=f[x].b then
begin
t:=f[x].a;
ff:=true;
exit;
end;
mid:=(f[x].a+f[x].b)>>1;
if f[x*2].c>=y then search(x*2,y)
else
if ((f[x*2].rm+f[x*2+1].lm>=y) and (not ff)) then 
begin
t:=mid-f[x*2].rm+1;
ff:=true;
exit;
end
else
if ((f[x*2+1].c>=y) and (not ff)) then search(x*2+1,y);
check(x);
end;


procedure deal(x,l,r:longint);
var mid:longint;
begin
xiugai(x);
if (f[x].a=l) and (f[x].b=r) then
begin
f[x].c:=r-l+1;
f[x].lm:=r-l+1;
f[x].rm:=r-l+1;
f[x].d:=1;
exit;
end;
mid:=(f[x].a+f[x].b) div 2;
if l>mid then deal(x*2+1,l,r)
else
if r<=mid then deal(x*2,l,r)
else
begin
deal(x*2,l,mid);
deal(x*2+1,mid+1,r);
end;
check(x);
//writeln(f[x].a,' ',f[x].b,' ',f[x].c,' ',f[x].lm,' ',f[x].rm);
end;

procedure check2(x,l,r:longint);
var mid:longint;
begin
xiugai(x);
if (f[x].a=l) and (f[x].b=r) then
begin
f[x].c:=0;
f[x].lm:=0;
f[x].rm:=0;
f[x].d:=-1;
//writeln(f[x].a,' ',f[x].b);
exit;
end;
if f[x].a=f[x].b then exit;
mid:=(f[x].a+f[x].b) div 2;
if l>mid then check2(x*2+1,l,r)
else
if r<=mid then check2(x*2,l,r)
else
begin
check2(x*2,l,mid);
check2(x*2+1,mid+1,r);
end;
check(x);
end;


begin
readln(n,m);
built(1,1,n);
for i:=1 to m do
begin
read(k);
if k=1 then 
begin
readln(x);
ff:=false;
if f[1].c<x then writeln(0) else begin t:=0; search(1,x); writeln(t); check2(1,t,t+x-1);  end;
end
else
begin   
readln(x,y);
deal(1,x,x+y-1);
end;
end;

end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 题目描述 有N头奶牛,它们在M个牛棚之间相互转移。每个牛棚里有一些奶牛,每分钟可以容纳一头奶牛。一头奶牛从一个牛棚走到另一个牛棚需要一分钟的时间。现在,这些奶牛要开一个牛派对,它们要在同一时间到达同一个牛棚,所以它们需要在某个牛棚等待一段时间。你需要计算最小的等待时间,使得所有奶牛都能够在同一时间到达同一个牛棚。 输入格式 第一行包含三个整数N,M,X。 接下来M行,每行包含三个整数a,b,t,表示牛棚a和牛棚b之间有一条双向边,需要t分钟才能通过。 输出格式 输出一个整数,表示最小等待时间。 数据范围 1≤N≤500 1≤M≤10000 1≤X≤N 1≤a,b≤N 1≤t≤1000 输入样例#1 3 3 1 1 2 5 2 3 5 1 3 10 输出样例#1 5 输入样例#2 4 5 4 1 2 10 2 3 10 3 4 10 4 1 10 1 3 20 输出样例#2 30 算法1 (最短路) $O(N^3)$ Dijkstra算法 Dijkstra(迪杰斯特拉)算法是由荷兰计算机科学家狄克斯特拉于1956年发明的,因此又叫狄克斯特拉算法。 Dijkstra算法是一种贪心算法,用于求解一个节点到其他所有节点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。 具体做法是:设立一个数组dis来保存源点到各个顶点的最短距离和一个数组book[i]来记录一个顶点是否已经在队列中。 初始时,原点s的路径权重被赋为0 (dis[s] = 0)。若对于顶点s存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,所有顶点并不属于任何已知最短路径所包含的顶点集合,因此都被标记为未知最短路径长度。当算法结束时,dis[v]中存储的便是源点s到顶点v的最短路径,或者如果从s无法到达v,则值为INF。 Dijkstra算法流程: 算法流程: 1. 将所有顶点分为两部分:已知最短路的顶点集合P和未知最短路的顶点集合Q。 2. 初始时,顶点集合P中只有源点s一个元素,以源点s为起点向外扩展。 3. 每次从顶点集合Q中选取一个顶点u(u的dist最小),并加入到顶点集合P中,同时以u为中心进行扩展。 4. 重复步骤3,直到顶点集合Q为空或者终点被加入到顶点集合P中。 5. 算法结束,最短路径保存在dis数组中。 时间复杂度 Dijkstra算法的时间复杂度为O(N^2)。由于N较小,因此可以通过本题。 参考文献 Dijkstra算法讲解 C++ 代码 算法2 (最短路) $O(N^2)$ Floyd算法 Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。 Floyd算法的基本思想 设G=(V,E)是一个带权有向图,其邻接矩阵为W。V={v1,v2,……,vn},W[1:n,1:n],则该图的Floyd算法可描述如下: 时间复杂度 Floyd算法的时间复杂度为O(N^3)。由于N较小,因此可以通过本题。 参考文献 Floyd算法讲解 C++ 代码 算法3 (最短路) $O(N^2)$ Bellman-Ford算法 Bellman-Ford算法是一种单源最短路径算法,可以处理负权边,但不能处理负权回路。 Bellman-Ford算法的基本思想 对于图中的任意一条边(u, v),Bellman-Ford算法会对每一条边进行一次松弛操作(Relax),并且这些操作是按照顺序进行的:当算法进行第i次松弛操作时,它只会改变长度为i+1的路径上的顶点的值。因此,当算法执行完第n-1次松弛操作后,路径长度最长不超过n-1,此时所有最短路径都已经求出。 时间复杂度 Bellman-Ford算法的时间复杂度为O(N*M)。由于N和M的范围较小,因此可以通过本题。 参考文献 Bellman-Ford算法讲解 C++ 代码 ### 回答2: Usaco 2007 Feb的问题是关于Cow Party的。这个问题中,农夫约翰有N头奶牛,它们之间通过一些路径相互连接,并且每个路径都有一个长度。约翰想要在某个时间将它的所有奶牛聚集在一起举办一个派对,现在他想知道所有奶牛从各自的位置到达聚会地点所需的最短时间。 为了解决这个问题,我们可以使用Dijkstra算法。我们首先需要创建一个节点集合,包含所有的奶牛和派对地点,并且初始化每个节点的最短时间为无穷大。接下来,我们选取一个起点节点--聚会地点,并将它的最短时间设置为0。然后我们开始遍历所有的节点,每次选择一个最短时间未确定的节点,并更新它的邻居节点的最短时间。我们重复这个过程,直到所有节点的最短时间都确定。 在更新节点的最短时间时,我们需要根据节点之间的路径长度来更新。我们检查从当前节点到邻居节点的路径长度加上当前节点的最短时间是否小于邻居节点目前的最短时间。如果是,则更新邻居节点的最短时间为新的最短时间。 最后,我们可以得到所有奶牛到达聚会地点所需的最短时间。我们找到所有奶牛起始位置的最长最短时间,即为我们的答案。 通过使用Dijkstra算法,我们可以解决这个问题并得到最优解。因此,Usaco 2007 Feb的Cow Party问题可以通过这种方法解决。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值