hrbust/哈理工oj 2110 Keys【dfs+巧妙的二维判重】

Keys
Time Limit: 1000 MSMemory Limit: 65536 K
Total Submit: 13(5 users)Total Accepted: 5(4 users)Rating: Special Judge: No
Description

    John is on a mission to steal some documents of importance from a one-story building. He has managed to get hold of a detailed floor plan of the building, indicating the locations of all the documents. There are doors in the building that require a key to be opened. John has some keys in his possession, and there are some keys in the building itself. Can you help him figure out how many documents he can steal?

Input

On the first line one positive number: the number of test cases, at most 100. 

After that per test case:

# one line with two space-separated integers h and w (2 <= h; w <= 100): the height and width of the map.

# h lines with w characters describing the building:

    ## ’.’ is an empty space.

    ## ’*’ is an impenetrable wall.

    ## ’$’ is a document that John would like to steal.

    ## an uppercase letter is a door.

    ## a lowercase letter is a key that opens all doors corresponding to its uppercase equivalent.

# one line with a string consisting of distinct lowercase letters: the keys that John has in his possession. If he has no keys, the line contains “0” instead.

    John can freely move around the outside of the building. For any given door, the number of available keys that open it can be zero, one, or more than one. For any given key, the number of doors that it opens can be zero, one or more than one. Keys can be reused.


Output

Per test case:

# one line with an integer: the total number of documents that John can steal.

Sample Input
3
5 17
*****************
.............**$*
*B*A*P*C**X*Y*.X.
*y*x*a*p**$*$**$*
*****************
cz
5 11
*.*********
*...*...*x*
*X*.*.*.*.*
*$*...*...*
***********
0
7 7
*ABCDE*
X.....F
W.$$$.G
V.$$$.H
U.$$$.J
T.....K
*SQPML*
irony
Sample Output
3
1
0


题目大意:给你一个n*m的图四个边缘的点可以当做入口,最初有一些钥匙,对应小写字母是指钥匙,对应大写字母是门,e.g:x钥匙能够开X的门。

思路:


对于入口的处理,最开始的思路是对这些出口放在一个结构体里边存上,当走到入口的时候,可以通向另外的入口。但是写了一发,TLE了,毕竟100*100的图的时候入口还是蛮多的,所以每一次走到入口处都遍历其他入口很耗时,所以这种思路是AC不掉的,后来从学长那里问到了正确思路,其实很简单,比如样例1,我们将图改变成这样就能简单解决了这个问题:


..................
.*****************.
..............**$*.
.*B*A*P*C**X*Y*.X..
.*y*x*a*p**$*$**$*.
.*****************.
...................

这样既能解决起点不确定的问题,还能解决超时的问题、


然后说判重问题,如果我们用状压来写,100*100*2^26的数组是开不出来的,所以状压+bfs的思路可以pass掉了,我们可以用vis【】【】数组+dfs来解决这个问题,如果遇到了新的钥匙,加入钥匙的同时,加上memet(vis);的操作就可以完美解决了这个问题、因为在没有拿到新钥匙之前,再重复走一个点是没有意义的,但是如果拿到了新钥匙,那么就有重复走一个点的必要,当然这个时候也要让output($)重新变成0,避免重复拿取的情况。


关键的问题都解决了,后边就是要仔细的写代码了。


#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
char aa[105][105];
char a[105][105];//加了一圈.之后的图
int n,m;
int vis[105][105];//二维判重
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int key[105];//钥匙数组,数据1表示有这个钥匙,0表示没有
int output;
int check(int x ,int y)//判断走到的地方是否符合条件
{
    if(x>=0 &&x<=n+1 &&y>=0 &&y<=m+1 &&vis[x][y] == 0 )
        return 1;
    return 0;
}
void dfs(int x,int y)
{
    if(a[x][y]=='*')return ;
    vis[x][y]=1;
    if(isupper(a[x][y]))//如果遇到了门
    {
        if(key[a[x][y]-'A']==0)return ;//没有对应的钥匙要return
        else//如果有钥匙就继续向下走
        {
            for(int i=0;i<4;i++)
            {
                if(check(x+fx[i],y+fy[i]))
                {
                    dfs(x+fx[i],y+fy[i]);
                }
            }
        }
    }
    if(a[x][y]=='$')//遇到钱和.都是一样的,只是遇到了钱output计数器要加一
    {
        output++;
        for(int i=0;i<4;i++)
        {
            if(check(x+fx[i],y+fy[i]))
            {
                dfs(x+fx[i],y+fy[i]);
            }
        }
    }
    if(a[x][y]=='.')
    {
        for(int i=0;i<4;i++)
        {
            if(check(x+fx[i],y+fy[i]))
            {
                dfs(x+fx[i],y+fy[i]);
            }
        }
    }
    if(islower(a[x][y]))//如果遇到了钥匙
    {
        if(key[a[x][y]-'a']!=0)//如果有这把钥匙就忽略掉继续走
        {
            for(int i=0;i<4;i++)
            {
                if(check(x+fx[i],y+fy[i]))
                {
                    dfs(x+fx[i],y+fy[i]);
                }
            }
        }
        else//否则要对vis清零,并且也要对计数器清零.
        {
            output=0;
            key[a[x][y]-'a']++;
            memset(vis,0,sizeof(vis));
            for(int i=0;i<4;i++)
            {
                if(check(x+fx[i],y+fy[i]))
                {
                    dfs(x+fx[i],y+fy[i]);
                }
            }
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(aa,'.',sizeof(aa));
        memset(key,0,sizeof(key));
        memset(vis,0,sizeof(vis));
        memset(a,'.',sizeof(a));
        for(int i=0;i<n;i++)
        {
            scanf("%s",aa[i]);
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                a[i+1][j+1]=aa[i][j];
            }
        }
        char tmp[50];
        scanf("%s",tmp);
        if(tmp[0]!='0')
        {
            for(int j=0;j<strlen(tmp);j++)key[tmp[j]-'a']++;
        }
        output=0;
        dfs(0,0);
        printf("%d\n",output);
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值