(HDU 2821)Pusher DFS + 在一个方向上可以移动多位

41 篇文章 0 订阅
27 篇文章 0 订阅

Pusher
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/65536 K (Java/Others)
Total Submission(s): 1508 Accepted Submission(s): 571
Special Judge

Problem Description
PusherBoy is an online game http://www.hacker.org/push . There is an R * C grid, and there are piles of blocks on some positions. The goal is to clear the blocks by pushing into them.

You should choose an empty area as the initial position of the PusherBoy. Then you can choose which direction (U for up, D for down, L for left and R for right) to push. Once the direction is chosen, the PusherBoy will walk ahead until he met a pile of blocks (Walking outside the grid is invalid). Then he remove one block from the pile (so if the pile contains only one block, it will become empty), and push the remaining pile of blocks to the next area. (If there have been some blocks in the next area, the two piles will form a new big pile.)

Please note if the pusher is right up against the block, he can’t remove and push it. That is, there must be a gap between the pusher and the pile. As the following figure, the pusher can go up, but cannot go down. (The cycle indicates the pusher, and the squares indicate the blocks. The nested squares indicate a pile of two blocks.)
And if a whole pile is pushed outside the grid, it will be considered as cleared.

Input
There are several test cases in each input. The first two lines of each case contain two numbers C and R. (R,C <= 25) Then R lines follow, indicating the grid. ‘.’ stands for an empty area, and a lowercase letter stands for a pile of blocks. (‘a’ for one block, ‘b’ for two blocks, ‘c’ for three, and so on.)

Output
Output three lines for each case. The first two lines contains two numbers x and y, indicating the initial position of the PusherBoy. (0 <= x < R, 0 <= y < C). The third line contains a moving sequence contains ‘U’, ‘D’, ‘L’ and ‘R’. Any correct answer will be accepted.

Sample Input
3
7


.b.


.a.

Sample Output
4
1
UDU
Hint
Hint: The following figures show the sample. The circle is the position of the pusher.
And the squares are blocks (The two nested squares indicating a pile of two blocks). And this is the unique solution for this case.

Source
2009 Multi-University Training Contest 1 - Host by TJU

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2821

题意:
在一个r*c的网格中,在某些位置上有一些堆块,用小写字母表示:a表示有1个,b表示有两个…;
其他得放都是空的,用’.’表示
开始时你可以任意选择一个空位置,然后可以进行四个方向上的运动,
每次选择一个方向后要一直运动,直到遇到了堆块,那么你会将堆块中的块数消除一个,并使得其他的堆块被加在这个方向上的下一个位置,你自己会处于堆块的位置
走出网格是非法的,并且想要清除某一方向堆块,你的位置和堆块之间至少要有1个空位
问你任意一种清除所有的堆块的方案?

注意:若堆块在某方向上网格的边缘位置,则在该方向上不能清除堆块

分析:
这一题就是一个基本的dfs,只不过要稍微复杂一点
对于初始位置的选择:
枚举所有的空位置为起始点,不过有一点优化:
假设(i,j)为我们所选的初始位置,初始方向为k,那么(i,j)满足:
在k方向上的第一个位置是空的
在k方向上的第二个位置是有堆块的
在k方向上的第三个位置还在网格内部

也就是说我们只选那些隔堆块最近的且满足题目条件的空位置为起始位置

DFS:
对于搜索,我们记录所有堆块的总数sum,和当前已经清除的堆块数cur
当 sum == cur 或者已经找到方案时就结束搜索

对于我们在一个方向上的处理:
首先想保证该方向上的第一个位置为空的,并且至少有3个格子在网格内部(不然无法将剩下的放到下一个位置上)
然后依次循环插着后面的位置,看是否有某个最近的位置上有堆块,并且不在网格边缘即可

当在一个方向上可以走多步时,只需要再加一个循坏(for(int j=1;j<=n;j++))表示所走的步数即可。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 30;
int m[maxn][maxn];
int step[1000];
int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
char d[] = "UDLR";
int r,c,sum,ansx,ansy;
bool flag;

bool judge(int x,int y)
{
    if(x < 0 || x >= r || y < 0 || y >= c) return false;
    else return true;
}

void dfs(int x,int y,int cur)
{
    if(cur == sum)
    {
        flag = 1;
        return ;
    }
    if(flag) return ;

    for(int k=0;k<4 && !flag;k++)
    {
        int x1 = x + dir[k][0], y1 = y + dir[k][1];
        if(m[x1][y1]) continue;  //若在该方向上相邻位置有堆块,则该方向无法移动
        for(int j=2;!flag;j++)   //在该方向上寻找可清除的堆块,j代表走的格子数
        {
            int x3 = x + (j+1) * dir[k][0], y3 = y + (j+1) * dir[k][1];
            if(!judge(x3,y3)) break;  //若想清除的位置在网格边界则也不能清除
            int x2 = x + j * dir[k][0], y2 = y + j * dir[k][1];
            if(m[x2][y2])
            {
                step[cur] = k;
                int t = m[x2][y2];
                m[x3][y3] += t - 1;
                m[x2][y2] = 0;
                dfs(x2,y2,cur+1);
                m[x2][y2] = t;     //还原数据
                m[x3][y3] -= t - 1;
                break;
            }
        }
    }
}

int main()
{
    char s[30];
    while(scanf("%d%d",&c,&r)!=EOF)
    {
        sum = 0;
        for(int i=0;i<r;i++)
        {
            scanf("%s",s);
            for(int j=0;j<c;j++)
            {
                if(s[j] == '.') m[i][j] = 0;
                else m[i][j] = s[j] - 'a' + 1;
                sum += m[i][j];
            }
        }
        flag = 0;
        for(int i=0;i<r && !flag;i++)
        {
            for(int j=0;j<c && !flag;j++)
            {
                if(!m[i][j])
                {
                    for(int k=0;k<4 && !flag;k++)
                    {
                        int x3 = i + 3 * dir[k][0], y3 = j + 3 * dir[k][1];
                        if(!judge(x3,y3)) continue;
                        int x1 = i + dir[k][0], y1 = j + dir[k][1];
                        int x2 = i + 2 * dir[k][0], y2 = j + 2 * dir[k][1];
                        if(m[x2][y2] && !m[x1][y1])
                        {
                            memset(step,-1,sizeof(step));
                            step[0] = k;
                            int t = m[x2][y2];
                            m[x3][y3] += t - 1;
                            m[x2][y2] = 0;
                            dfs(x2,y2,1);
                            if(flag)
                            {
                                ansx = i;
                                ansy = j;
                            }
                            else
                            {
                                m[x2][y2] = t;
                                m[x3][y3] -= t - 1;
                            }
                        }
                    }

                }
            }
        }
        printf("%d\n%d\n",ansx,ansy);
        for(int i=0;i<sum;i++) printf("%c",d[step[i]]);
        printf("\n");
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值