nyoj 667 Biggest Number 搜索 减枝 dfs bfs

2 篇文章 0 订阅
1 篇文章 0 订阅

题目:

Biggest Number

时间限制:1000 ms  |  内存限制:65535 KB

难度:4

描述

You have a maze with obstacles and nonzero 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 orwalk 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.

 

输入

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 numbe

r 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.

输出

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

样例输入

3 7

##9784#

##123##

##45###

0 0

样例输出

791452384

来源

湖南省第六届大学生计算机程序设计竞赛

题目大意:

给一个图,求寻找一个最大的数。只能上下左右四个方向,且不能走走过的路。

题目思路:

因为时间只有1ns,所以单用dfs要超时,所以需要减枝

1、如果答案达到最大长度,且当前搜索的小于答案,直接跳出

2、每次dfs之前,用bfs来确定剩下多少个数字,如果小于答案,直接跳出

3.

程序:

 

 
#include<iostream>
#include<cstdio>
#include<cstring>using namespace std;
struct node
{
    int x,y;
} queue[1000];
int n,m;
int Max,flag,tatal; //当前检索到的最大值的长度、当前检索值首数字与当前检索到的最大值的首数字的关系、数字总数
char map[20][20]; //输入地图存储
char ans[35],stack[35]; //当前检索到的最大值、当前检索值的情况
int dir[4][2]= {-1,0,0,-1,0,1,1,0}; //四个方向
bool yes(int x,int y)
{
    return x>=0&&x<n&&y>=0&&y<m; //该点是否在地图中,是就返回true
}
int bfs(int x,int y) //(x,y)处往后还能搜索到多少位有效数字
{
    node t;
    char g[20][20];
    for(int i=0; i<n; i++)strcpy(g[i],map[i]); //将g中的值赋为map
    int head,tail; //队列前后”指针“
    head=tail=0; //队列为空
    t.x=x,t.y=y; //其实节点,用于入队
    queue[tail++]=t; //数组模拟队列
    while(head<tail) //队列不为空
    {
        x=queue[head].x;
        y=queue[head++].y; //取出队头元素,队头后移一位
        for(int i=0; i<4; i++) //四面查找
        {
            int xx=x+dir[i][0];
            int yy=y+dir[i][1];
            if(!yes(xx,yy)||g[xx][yy]=='#')continue; //该点不在地图中,该点不可处理【不处理该点】
            g[xx][yy]='#'; //要处理该点,标记,预防重处理
            t.x=xx,t.y=yy; //记录情况
            queue[tail++]=t; //新节点入“队”
        }
    }
    return head; //该次处理的点数
}
void dfs(int x,int y,int cnt) //当前检索点的坐标情况,当前检索到的数的长度
{
    if(Max<cnt||(Max==cnt&&flag==1)) //当前检索到的数的长度大于当前检索到的最大的数的总长度“或”长度相等但是当前值首数字比较大
        //当前检索到的值必定大于当前检索到的“最大值”【强剪枝2】
    {
        stack[cnt]=0; //字符串结束标志【别忘了,字符数组没有结束标志会输出错误的】
        strcpy(ans,stack); //将当前值赋给”最大值“
        Max=cnt; //改变最大值的长度
        flag=0; //大小不确定
    }
    int res=bfs(x,y); //该点后面还能搜索到res个有效数字
    if(res+cnt-1<Max||(res+cnt-1==Max&&flag==-1))return; //若该点参与的检索中的有效数字的最大长度小于当前检索到的数字中的最大值的长度
    //长度相等,但是该次检索的数字要小【这个数肯定小于当前检索到的最大值】直接结束搜索【强剪枝3】
    for(int i=0; i<4; i++) //当前的搜索得到的数肯定会大于当前检索到的最大值,【搜索继续】
    {
        int xx=x+dir[i][0];
        int yy=y+dir[i][1];
        if(!yes(xx,yy)||map[xx][yy]=='#')continue; //该点不在地图中,或该点不可处理【不作处理】【常规剪枝】
        if(flag!=1&&ans[cnt]>map[xx][yy]&&tatal==Max)continue; //当前检索的值不可能大于当前检索到的最大值【强剪枝4】
        stack[cnt]=map[xx][yy]; //检索到的数字可用,加入到临时【当前检索值的情况】中
        map[xx][yy]='#'; //记得标记哦,检索过的就不要再检索了
        if(flag==0) //【】【】大小不确定
        {
            if(cnt>=Max)flag=1; //长度,当前检索到的比较大
            else if(ans[cnt]==stack[cnt])flag=0; //长度想相同就比首字母 【大小不确定】
            else if(ans[cnt]<stack[cnt])flag=1; //当前检索到的i较大
            else flag=-1; //当前检索到的比较小
            dfs(xx,yy,cnt+1); //长度加1
            flag=0;//【大小不确定】
        }
        else dfs(xx,yy,cnt+1);   //若大小确定,继续检索
        map[xx][yy]=stack[cnt]; //搜索归来,抹去标记
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)&&n+m)
    {
        tatal=0;
        for(int i=0; i<n; i++)
        {
            scanf("%s",map+i);
            for(int j=0; j<m; j++)
            {
                if(map[i][j]!='#')tatal++;
            }
        }
        Max=1;
        memset(ans,0,sizeof(ans));
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                if(map[i][j]=='#')continue;
                if(Max==tatal&&ans[0]>map[i][j])continue; //当前检索到的最大值,长度已达到最长&&开头数字比当前检索数字大
                 //以当前数字开头检索到的最大值肯定小于当前检索到的最大值【强剪枝1】
                stack[0]=map[i][j];
                map[i][j]='#'; //访问标记
                if(ans[0]==stack[0])flag=0;//大小不确定
                else if(ans[0]<stack[0])flag=1; //大于
                else flag=-1; //小于
                dfs(i,j,1);
                map[i][j]=stack[0];
//搜索归来,抹掉标记
            }
        }
        printf("%s\n",ans);
//输出最终答案
    }
    return 0;
}
//原码转自:http://blog.csdn.net/andring/article/details/7376129
        

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值