【测试】【20110422】
题目
2010年辽宁OI选拔赛
题目一、洪水
我们知道,全球变暖使得海平面上升。OI小岛的岛主请OIer们解答水位达到多高时,该岛将会变成两个或更多的岛。
输入文件“flood.in”
给出小岛的高度网格数据:文件第一行为两个不超过100的正整数n和m,表示网格的维度。随后为n行数据,每行有m个不超过1000整数,表示网格节点的原始高度。网格最外圈的数据0及与其相邻的0表示最初的海平面。和外圈不相连的0是被高地包围的海平面高度。假定海洋最初围绕给定的网格,小岛初始时是连通的。
输出文件“flood.out”
Island splits when ocean rises f feet.
或者
Island never splits.
(注意:如果上面的f=5,更为精确地说是海平面升高5英尺多一点点小岛会分解。)
示例输入1:
5 5
3 4 3 0 0
3 5 5 4 3
2 5 4 4 3
1 3 0 0 0
1 2 1 0 0
示例输出1:
Island never splits.
示例输入2:
5 5
5 5 5 5 7
4 1 1 1 4
4 1 2 1 3
7 1 0 0 4
7 3 4 4 4
示例输出2:
Island splits when ocean rises 3 feet.
题目二、游泳
聪聪新家附近的游泳池仅有两个泳道,每个泳道只能单向游。游泳者被要求在一个泳道上开始游,从一端游到另一端,然后改变泳道游回来。当游泳者到达最初游的一端后,应该返回到其初始泳道上,然后再开始游新一圈。
每个游泳者有其自己的常规速度,游泳者不允许在泳道中间超过其他游泳者,除了在泳道的两端,因为泳道没有足够的宽度,中间超越可能引发事故。如果一个较快游泳者前面有另一个较慢的游泳者,快者只能改变速度跟随慢者直到泳道的前端。注意堵道的游泳者的常规速度可能比被堵的要快,因为堵道的游泳者也会被其前面另一个速度更慢的游泳者给堵住了。
如果同时到达泳道的一端,游泳者可以改变他们的顺序,即快者游到了慢者的前面。当由俩个或更多的人在泳道一端堵塞时,可看作是同时到达,因而在端处他们可以马上改变前后顺序。
给定游泳者的数量n、每个游泳者的常规速度(以其从一端游向另一端所花费的时间ti表示)及游泳者打算游泳的圈数ci(注意这里的一圈指的是从一端游到另一端然后再游回初始一端)。你的任务是计算所有游泳者完成他们计划圈数所需的时间。假定所有的游泳者从相同的一端同时开始游,快者游在前面。这里可以忽略游泳者身体尺寸,还可忽略每个游泳者在泳道端处换泳道的时间及一组游泳者在泳道端处的顺序。
输入文件swimming.in
n
t1 c1
…
tn cn
n是一个整数(1≤n≤50),为游泳者的数量。ti和ci是整数(1≤ti<300, 1≤ci≤250)分别表示第i个游泳者从一端到另一端所需的时间和计划圈数,ti和ci间用空格分开。
输出文件swimming.out
输出所用游泳者完成他们的游泳计划所需时间。
示例输入:
4
2 4
7 2
8 2
18 1
示例输出:
40
分析
第一题一看就是一个floodfill,但是有顺序,由题意可知:如果水面上升到一个高度,那么该图中小于等于该高度,且相连,且和海边相连(最外圈的点)的岛屿都能被淹没。那么我们定下一个高度,从最外层开始floodfill即可。之后要判断是否将剩下的岛屿分成了两部分。就再从图中找到刚才没有被搜索到的点,再进行一次floodfill找到所有相连的点,看两次搜到的点的个数总和是否等于总点数即可。
对于高度的确定,采取二分答案的方法即可。应该可以将时间复杂度控制在范围内。
第二题,一看就是模拟。
那么怎样表示状态就成了问题,如果我们把时间转成速度,很难实现。
那么之间用事件处理,开一个二维数组time[i,j]表示第i个人游完他的第j次,需要的时间。如果它游完上一次的时间加上它游当前次的的时间小于在该泳道的的人的时间,则一定被该泳道的人挡住,且被其中最慢的人挡住。那么它游完当前次的时间就是挡住他的人的时间。
按照什么顺序处理这些人呢?
为了保证速度快的人先走,我们要将这n个人按照其时间排序。每次从每个人的最后一趟中找到当前用时最短的,也就是说他接下来的运动状态不会受其他任何人的影响,且其下一状态没有被更新过。实质上就是按照时间顺序处理这n个人。
code
program liukee; var a:array[0..105,0..105] of longint; hash1,hash2:array[0..105,0..105] of boolean; n,m,l,r,sum1,sum2,op,mid:longint; procedure init; var i,j:longint; begin readln(n,m); for i:=1 to n do for j:=1 to m do begin read(a[i,j]); if a[i,j]>r then r:=a[i,j]; end; end; procedure dfs1(x,y:longint); begin if (a[x,y]>mid)or(x<1)or(y<1)or(x>n)or(y>m)then exit; if hash1[x,y] then exit; inc(sum1); hash1[x,y]:=true; dfs1(x,y+1);dfs1(x+1,y); dfs1(x,y-1);dfs1(x-1,y); end; procedure dfs2(x,y:longint); begin if(x<1)or(y<1)or(x>n)or(y>m)then exit; if hash1[x,y] then exit; if hash2[x,y] then exit; inc(sum2); hash2[x,y]:=true; dfs2(x,y+1);dfs2(x+1,y); dfs2(x,y-1);dfs2(x-1,y); end; function check:boolean; var i,j:longint; begin fillchar(hash1,sizeof(hash1),0); fillchar(hash2,sizeof(hash2),0); sum1:=0; sum2:=0; for i:=1 to n do begin dfs1(i,1); dfs1(i,m); end; for i:=1 to m do begin dfs1(1,i); dfs1(n,i); end; for i:=1 to n do for j:=1 to m do begin if not hash1[i,j] then begin dfs2(i,j); if (sum1+sum2=n*m) then exit(false) else exit(true); end; end; end; begin assign(input,'flood.in');reset(input); assign(output,'flood.ans');rewrite(output); r:=0; init; l:=0; op:=-1; while l<=r do begin mid:=(l+r)>>1; if check then begin op:=mid; r:=mid-1; end else l:=mid+1; end; if op=-1 then writeln('Island never splits.') else writeln('Island splits when ocean rises ',op,' feet.'); close(input); close(output); end.
program liukee; var a,b,sum:array[1..60] of longint; time:array[0..60,0..600] of longint; n,i,j,x,ans:longint; procedure init; var i:longint; begin readln(n); for i:=1 to n do readln(a[i],b[i]); end; procedure sort(l,r:longint); var i,j,mid,temp:longint; begin i:=l; j:=r; mid:=a[(l+r)>>1]; repeat while a[i]<mid do inc(i); while a[j]>mid do dec(j); if i<=j then begin temp:=a[i];a[i]:=a[j];a[j]:=temp; temp:=b[i];b[i]:=b[j];b[j]:=temp; inc(i); dec(j); end; until i>j; if l<j then sort(l,j); if i<r then sort(i,r); end; function check:boolean; var i:longint; begin check:=false; for i:=1 to n do if time[i,0]<b[i]*2 then exit(true); end; function find:longint; var i:longint; pig:longint; begin pig:=maxlongint; for i:=1 to n do if time[i,time[i,0]]<pig then if time[i,0]<b[i]*2 then begin pig:=time[i,time[i,0]]; find:=i; end; end; begin assign(input,'swimming.in');reset(input); assign(output,'swimming.ans');rewrite(output); init; sort(1,n); for i:=1 to n do begin time[i,1]:=a[i]; time[i,0]:=1; end; while check do begin x:=find; time[x,time[x,0]+1]:=time[x,time[x,0]]+a[x]; inc(time[x,0]); for i:=x to n do if odd(time[x,0])=odd(time[i,0])then if time[i,time[i,0]]>time[x,time[x,0]] then time[x,time[x,0]]:=time[i,time[i,0]]; ans:=time[x,time[x,0]]; end; writeln(ans); close(input); close(output); end.
分析
第一题要注意题目中的条件,方法很显然,但是要理解题意,明白如何搜索。
第二题是模拟,但要注意恰当的表示状态,要明白全过程,注意模拟的顺序。
总体来说代码难度不大,但思维量很大。