2017.08.18【NOIP提高组】模拟赛B组 沙耶的玩偶(doll)

####Description

在美鱼和理树后援团拯救世界的同时,外表柔弱的理树也开始坚强起来,思考着离开这个世界的办法。误打误撞地,她遇上了正在教室破坏课桌打开迷宫入口的沙耶。沙耶告诉理树,这个世界的出口就是这个迷宫的出口。于是理树毫不犹豫地跟沙耶一起跳进了迷宫。在迷宫里,两个女孩子互帮互助,一会儿割绳子,一会儿泡温泉,一会儿雕冰块,跌跌撞撞地走到了终点。不出所料,终点也有一个机关在等着她们。

终点的机关是一个立着的mn 的方格棋盘,在有些格子上放了一个玩偶,而有些地方直接挖了个大坑。只有取走所有玩偶才能打开出口。但是,由于奇怪的设定,理树和沙耶不能直接触碰玩偶,他们需要操纵机器人来收集它。机器人的走法很奇怪,和国际象棋的马有点像,只不过马可以走任意方向的12 路线,它们只会由上往下走rc(或cr)的路线,不能回头。而机器人一旦经过一个有玩偶的格子,那个格子上的玩偶将被回收,并且在机器人离开时,那个格子会变成一个坑。理树可以把机器人放在任何一个有玩偶的格子上作为起点,也可以在任何一个有玩偶的格子回收机器人。机器人行走可以视为瞬移,只不过每一次设置新起点都会消耗1 时间。并且,有坑的格子不能落脚。

就在这个紧要关头,玩偶狂热爱好者的沙耶却流着口水智商归0。理树不得不转而求助你,帮忙计算出最少多少时间就能收集到所有玩偶。

####Input

第一行包含4 个整数M、N、R、C,意义见问题描述。接下来M 行每行一个长度为N 的

字符串。如果某个字符是’.’,表示这个地方有一个玩偶;如果这个字符是’x’,表示这个地

方是坑。

####Output

输出一个整数,表示最短时间。

####Sample Input

3 3 1 2

.x.

####Sample Output

4

####Data Constraint

30%的数据中,1<=M,N<=4,1<=R,C<=3。

70%的数据中,1<=M<=20,1<=N<=4,1<=R,C<=3。

100%的数据中,1<=M,N<=50,1<=R,C<=10。

####Hint

这里写图片描述

####题解:
先观察题目要求,如果当前机器人在格子(X,Y),即第 X 行第 Y 列,那么它能到
达的地方最多四个:(X+R,Y+C),(X+R,Y-C),(X+C,Y+R),(X+C,Y-R)。当然,
其中某些格子可能是不能到达的或者超越了边界。
用一个简单的迭代深度优先搜索解决。迭代需要总时间数,然后让每个机器人通
过深搜的形式遍历棋盘,然后判断棋盘是否已经被全部覆盖。由于 M、N、R、C
都很小,所以效果不错。加上适当的剪枝,或许可以多过一点测试数据。(30 分)

考虑到 M 很大,N 却很小。因此想到状态压缩。也可以发现 R、C 也很小,因此
状态压缩应该可行。
观察每一行,这一行上的格子最多只能由它上面 Max{R,C}行的格子跳过来,而
在再上去就跟它无关了。于是可以状态压缩这 N*Max(R,C)规模的矩阵,用 01 串
表示这个格子当前是否存在机器人,然后将其压缩成一个数存储起来。
对于新的一行,上面的空格子必须全部放机器人,所以一定是由上面 Max(R,C)
行中的一些格子跳过来。但是是由哪些格子跳过来的呢?这些格子由分别跳向哪
些格子?这个无法得知。不过,可以想到上面 Max(R,C)中选出来的格子和当前
新一行中的空格子一定是一一对应的。于是想到二分图匹配的方法,让新一行中
所有空格子与上面 Max(R,C)行中的可以跳过来的格子做最大匹配。求出最大匹
配后,把那些匹配上了的格子清除机器人,并在新的一行中建立机器人。
如果最大匹配<新一行中空格子个数,那么必须新建一些机器人来保证覆盖全
图,这个答案加到动态规划的答案中。(70 分)

由于70分上看到二分图匹配,于是就想用最大图匹配算法来弄。
前提是当前机器人在格子(X,Y),即第 X 行第 Y 列,那么它能到达的地方最多四个:(X+R,Y+C),(X+R,Y-C),(X+C,Y+R),(X+C,Y-R)。
那么把问题求机器人路径覆盖整个盘子想象成求整个图的最小路径覆盖。而且观察
此题是有向无环图,所以可以使用二分图最大匹配的方法。
从某个点去更新下一个点,当某个点被匹配了之后,考虑最大图匹配中的向后跳,也就是把之前更新它的点的向其他地方跳。
左边建立 MN个点,右边建立 MN 个点。如果(X,Y)可以跳向(X’,Y’),那么就
让左边的(X,Y)向右边的(X’,Y’)连一条边。
建好图后,做一次最大匹配。那么最终答案就是空格子个数-最大匹配数。(100分)
下面是匈牙利算法:

####Code


var
        i,j,k,l,n,m,r,c,gs,x,y,ans:longint;
        edgs:array[1..5000,0..5] of longint;
        map:array[0..100,0..100] of char;
        bh:array[0..100,0..100] of longint;
        fx:array[1..4,1..2] of longint;
        flag,b:array[1..5000]of longint;
        pd:boolean;
function can(x:longint):boolean;
var
        ii:longint;
begin
        if flag[x]=i then exit(false);
        flag[x]:=i;
        for ii:=1 to edgs[x,0] do
        begin
                if (b[edgs[x,ii]]=0)or (can(b[edgs[x,ii]]))then
                begin
                        b[edgs[x,ii]]:=x;
                        exit(true);
                end;
        end;
        exit(false);
end;
begin
        readln(n,m,r,c);
        fx[1,1]:=r;fx[1,2]:=c;
        fx[2,1]:=r;fx[2,2]:=-c;
        fx[3,1]:=c;fx[3,2]:=-r;
        fx[4,1]:=c;fx[4,2]:=r;
        for i:=1 to n do
        begin
                for j:=1 to m do
                begin
                        inc(gs);
                        bh[i,j]:=gs;
                        read(map[i,j]);
                        if map[i,j]='.' then inc(ans);
                end;
                readln;
        end;
        for i:=1 to n do
        begin
                for j:=1 to m do
                begin
                        if (i=1) and (j=4) then
                        begin
                                pd:=false;
                        end;
                        pd:=false;
                        if map[i,j]='.' then
                        for k:=1 to 4 do
                        begin
                                x:=fx[k,1]+i;
                                y:=fx[k,2]+j;
                                if (x>0) and (y>0) and (x<=n) and (y<=m) and (map[x,y]='.') then
                                begin
                                        inc(edgs[bh[i,j],0]);
                                        edgs[bh[i,j],edgs[bh[i,j],0]]:=bh[x,y];
                                end;
                        end;
                end;
        end;
        for i:=1 to gs do
        begin
                if edgs[i,0]>0 then
                begin
                        if can(i) then dec(ans);
                end;
        end;
        writeln(ans);
end.




我活在这夜里。无论周围多么黑暗,我都要努力发光!我相信着,终有一天,我会在这深邃的夜里,造就一道最美的彩虹。
原文地址:https://www.cnblogs.com/RainbowCrown/p/11148423.html