【NOIP2017提高组模拟12.18】C

题目

Description

给出一个H的行和W列的网格。第i行第j列的状态是由一个字母的A[i][j]表示,如下:
“.” 此格为空。
“o” 此格包含一个机器人。
“E” 此格包含一个出口,保证出口在整个网格中有且只有一个
每次可以选择上,下,左,右之一的方向,将所有剩余的机器人向这个方向移动一个格子,如果一个机器人被移出了网格,那么这个机器人会爆炸,并立即消失。如果一个机器人移动到出口所在的格子,机器人将获救,并消失,最多有多少机器人获救。

Input

第一行两个整数n,m
接下来一个n*m的字符矩阵,共n行,每行m个字符,每个字符之间无空格,字符意义如题所示。

Output

输出一个整数,即最大的获救的机器人的数量

Sample Input

【样例输入1】
3 3
o.o
.Eo
ooo
【样例输入2】
3 4
o…
o…
oooE

Sample Output

【样例输出1】
3
【样例输出2】
5

Data Constraint

对于20%的数据,n*m<=9
对于另外40%的数据,出口在网格图的最左上角,即第1行第1列。
对于100%的数据,n,m<=100

Hint

移动序列为左,上,右时,第2行第3个与第3行第2,3个机器人获救,其他机器人爆炸

题解

一道有一点点繁琐的DP题
可以设F[i,j,u,d]表示现在机器人向四个方向的最大位移是多少
那么在这么多位移之后曾经出过矩阵范围的机器人要么死了要么在出边界之前已经被救了
相似的,在E位置向扩张i个位置,向右扩张j个位置,向上扩张u个位置,向下扩张d个位置
这里写图片描述
如图,被红色和黄色覆盖的位置要么已经挂了,要么被救了,反正就是毛都不剩了
然后可能还有机器人存在的地方就是白色的区域
白色区域的范围很显然是可以通过i,j,u,d得到的
那么如果我们再向上牺牲一行,下面就会多一行获救,左,右,下同理
下面是一个例子:
这里写图片描述
紫色的区域就是把d+1之后可以获救的机器人区域
设e(x,y)
可以发现在哪一行十分好求,然后列的左边界为max(r,y-l),右边界min(y+r,m-d)
其他三种情况同理
注意一些边界上的细节

贴代码

var
    f:array[0..2,-1..101,-1..101,-1..101]of longint;
    a:array[0..101,0..101]of longint;
    sum1,sum2:array[-1..105,-1..105]of longint;
    i,j,k,l,m,n,x,y,r,u,d,p,ans:longint;
    ch:char;
function max(x,y:longint):longint;
begin
    if x>y then exit(x) else exit(y);
end;
function min(x,y:longint):longint;
begin
    if x>y then exit(y) else exit(x);
end;
begin
    //assign(input,'t3.in'); reset(input);
    readln(n,m);
    for i:=1 to n do
    begin
        for j:=1 to m do
        begin
            read(ch);
            if ch='o' then a[i,j]:=1;
            if ch='E' then
            begin
                x:=i;
                y:=j;
            end;
        end;
        readln;
    end;
    for i:=1 to n do
        for j:=1 to m do sum1[i,j]:=sum1[i,j-1]+a[i,j];
    for i:=1 to m do
        for j:=1 to n do sum2[i,j]:=sum2[i,j-1]+a[j,i];
    for l:=0 to y-1 do
    begin
        for r:=0 to m-y do
            for u:=0 to x-1 do
                for d:=0 to n-x do
                begin
                    if x+d+u+1<=n then
                    f[0,r,u,d+1]:=max(f[0,r,u,d+1],
                    f[0,r,u,d]+sum1[x+d+1,min(y+r,m-l)]-sum1[x+d+1,max(y-l,r+1)-1]);
                    ans:=max(ans,f[0,r,u,d+1]);
                    if y-l-1>r then
                    f[1,r,u,d]:=max(f[1,r,u,d],
                    f[0,r,u,d]+sum2[y-l-1,min(x+d,n-u)]-sum2[y-l-1,max(x-u,d+1)-1]);
                    ans:=max(ans,f[1,r,u,d]);
                    if r+y+1+l<=m then
                    f[0,r+1,u,d]:=max(f[0,r+1,u,d],
                    f[0,r,u,d]+sum2[y+r+1,min(x+d,n-u)]-sum2[y+r+1,max(x-u,d+1)-1]);
                    ans:=max(ans,f[0,r+1,u,d]);
                    if x-u-1>d then
                    f[0,r,u+1,d]:=max(f[0,r,u+1,d],
                    f[0,r,u,d]+sum1[x-u-1,min(y+r,m-l)]-sum1[x-u-1,max(y-l,r+1)-1]);
                    ans:=max(ans,f[0,r,u+1,d]);
                end;
        ans:=ans;
        f[0]:=f[1];
    end;
    writeln(ans);
  //  close(input);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值