ZCMU—1758

1758: Biggest Number

Time Limit: 1 Sec  Memory Limit: 128 MB
[Submit][Status][Web Board]

Description

You have a maze with obstacles and non-zero digits in it: You can start from any square, walk in the maze, and finally stop at some square. Each step, you may only walk into one of the four neighbouring squares (up, down, left, right) and you cannot walk into obstacles or walk into a square more than once. When you finish, you can get a number by writing down the digits you encounter in the same order as you meet them. For example, you can get numbers 9784, 4832145 etc. The biggest number you can get is 791452384, shown in the picture above. Your task is to find the biggest number you can get.

Input

There will be at most 25 test cases. Each test begins with two integers R and C (2<=R,C<=15, R*C<=30), the number of rows and columns of the maze. The next R rows represent the maze. Each line contains exactly C characters (without leading or trailing spaces), each of them will be either '#' or one of the nine non-zero digits. There will be at least one non-obstacle squares (i.e. squares with a non-zero digit in it) in the maze. The input is terminated by a test case with R=C=0, you should not process it.

Output

For each test case, print the biggest number you can find, on a single line.

Sample Input

3 7##9784###123####45###0 0

Sample Output

791452384

HINT

 

 

 

 

 

题意:有一个R*C的矩阵,矩阵里有1~9的数字,#表示墙,从矩阵任意一点出发,只能向上下左右移动,格子不能重复到达,把走过路径里的数字按位连起来组成一个很大的数字,求最大的数字。

【分析】
题意非常明确,一开始想到的是,连起来的位数越长,就越大(后来发现这并没有什么大用...)因为有30个点,bfs会炸内存,所以选择dfs,但是显然这道30个点的路径搜索加上每次需要按位比较数组,超时是一定的所以需要优化。

我觉得一般的优化意义不大,对于这种大数据优化还是比较明显需要剪枝....

第一个剪枝,非常简单,就是如果当前长度加上剩下可到达的位置所有长度加起来都小于当前解的长度,那就可以剪掉。这是最简单的优化,也比较暴力...无所谓判断,用一个很简单的路径覆盖的搜索去搜剩下的长度,虽然需要一个搜索去搜长度,但是能够剪掉的时间远远大于这个搜索。

然而一个剪枝并不够。。还是TLE所以只能继续想第二个优化。

第二个剪枝是基于第一个剪枝,既然已经剪掉了长度小的情况,那么长度和当前解的长度相同的时候,是不是也可以剪掉一部分呢,显然是可以的。

在搜索剩下长度的时候,边搜边比较,如果长度一样,那么就判断剩下路径中第一个和当前解答案不一样的位置,是大于当前解还是小于当前解,小于当前解,那么就可以直接退出,可能表达的不是很清楚,举个例子

比如当前搜到的是

1234,剩下可以搜到485

当前解是1234567

那么,在判断的时候 第一个与解不一样的位置是第五位也就是4,答案中是5.那么这个时候,长度一样,第一个不同位置比ans小,那就可以直接剪掉了。

因为显然12344**一定比1234567小。不管后面能搜到什么答案。

当时写的时候想到了一个自认为很巧妙的方法....在正常搜索的时候就直接记录一下当前这个位置和答案的大小,也就是代码里的bs变量。

这时候第二个剪枝就会变的非常简单,只是在第一个剪枝的if判断里多了一个条件而已

 

【代码】

#include <stdio.h>
#include <cstring> 
using namespace std;
 
 
char a[20][20];
int used[20][20]={0};
int usedd[20][20]={0};
int ans[200]={0};
int nowans[200]={0};
int bs,anslen=0,l,r,maxlen;
int move[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
 
void find(int x,int y,int len)
{
    if ((len>anslen)||(len==anslen && bs==1))
    {
        for (int i=0;i<len;i++) ans[i]=nowans[i];
        anslen=len;
        bs=0;
    }
     
    memset(usedd,0,sizeof(usedd));
    int maxlen=0,tot=1;
    int search[200][2]={0};
    search[0][0]=x;search[0][1]=y;
    usedd[x][y]=1;
    while (maxlen<tot)
    {
        for (int i=0;i<4;i++)
        {
            int xx,yy;
            xx=search[maxlen][0]+move[i][0];
            yy=search[maxlen][1]+move[i][1];
            if ((xx>=0)&&(yy>=0)&&(xx<l)&&(yy<r)&&(a[xx][yy]!='#')&&(used[xx][yy]==0)&&(usedd[xx][yy]==0))
            {
                usedd[xx][yy]=1;
                search[tot][0]=xx;
                search[tot][1]=yy;
                tot++;
            }
        }
        maxlen++;
    }
     
    if ((len+maxlen-1<anslen)||((len+maxlen-1==anslen) &&(bs==-1))) return;
     
     
    int xx,yy;
    for (int i=0;i<4;i++)
    {
        xx=x+move[i][0];
        yy=y+move[i][1];
        if ((xx>=0)&&(xx<l)&&(yy>=0)&&(yy<r)&&(used[xx][yy]==0)&&(a[xx][yy]!='#'))
        {
            nowans[len]=a[xx][yy]-48;
            used[xx][yy]=1;
            if (bs!=0) 
            {
                find(xx,yy,len+1);
            }
            else
            {
                if (len>=anslen)
                {
                    bs=1;
                    find(xx,yy,len+1);
                    bs=0;
                }
                else
                {
                    if(nowans[len]>ans[len])
                    {
                        bs=1;
                        find(xx,yy,len+1);
                        bs=0;
                    }
                    else
                    if (nowans[len]==ans[len])
                    {
                        bs=0;
                        find(xx,yy,len+1);
                        bs=0;
                    }
                    else
                    {
                        bs=-1;
                        find(xx,yy,len+1);
                        bs=0;
                    }
                }
            }
            used[xx][yy]=0;
        }
    }   
}
 
 
int main()
{
    scanf("%d %d",&l,&r);
    while (l!=0 && r!=0)
    {
        memset(ans,0,sizeof(ans));
        memset(nowans,0,sizeof(nowans));
        memset(used,0,sizeof(used));
        int i,j;
        for (i=0;i<l;i++) scanf("%s",&a[i]);
        for (i=0;i<l;i++)
            for (j=0;j<r;j++)
                if (a[i][j]=='#') used[i][j]=1;
        anslen=1;ans[0]=-1;
        for (i=0;i<l;i++)
            for (j=0;j<r;j++)
            if (a[i][j]!='#')
            {
                nowans[0]=a[i][j]-48;
                used[i][j]=1;
                if (nowans[0]>ans[0])
                {
                    bs=1;
                    find(i,j,1);
                }
                else
                if (nowans[0]==ans[0])
                {
                    bs=0;
                    find(i,j,1);
                }
                else
                if (nowans[0]<ans[0])
                {
                    bs=-1;
                    find(i,j,1);
                }
                used[i][j]=0;
                 
            }
        for (i=0;i<anslen;i++) printf("%d",ans[i]);
        printf("\n");scanf("%d %d",&l,&r);
    }
    return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值