uva Biggest Number UVA - 11882 || nbut [1464] Biggest Number(dfs强力剪枝)

Biggest Number

  UVA - 11882 
数据较弱
题目大意:有一个R*C的矩阵,矩阵里面有1~9的数字(太好了不用处理前导0),或者是#(代表不能通过),先要从矩阵任意一点出发(之前英语抓鸡看成了边界,英语差的孩纸伤不起啊>_<),只能往上下左右四个方向移动,每个格子不能重复走,到达矩阵内任意一点。把这条路径的数字连起来变成一个很大的数字,求这个数字最大是什么。
思路:DP?记忆化搜索?30个点噢,时间吃得消内存都吃不消啦。所以呢?搜索。还是超时啊?剪枝啊。
剪枝1:假设当前答案为ans,那么当我们走到一个点(x, y)的时候,作一个小小的搜索预判。假设现在能从(x, y)走到的点,我们都能到达,这是最好的情况。设从(x, y)能走到的点数为maxlen,那么如果从出发点走到(x, y)经过的格子,加上maxlen,都没有ans的长度大,那么不管从(x, y)怎么搜,我们都不能取代我们现在的ans(长才是王道懂不懂),那么直接回溯,不要这个点了。这个剪枝效力还是不错的,但是还是TLE,我试过了QAQ。最近有点脑残,明明可以做一个数据测试一下非要交上去试一下……
剪枝2:同剪枝1,假设当前答案为ans,那么当我们走到一个点(x, y)的时候,搜到maxlen(同剪枝1),如果从出发点走到(x, y)经过的格子,加上maxlen,大于ans的长度,我们就只能继续搜了……如果等于呢?那么就再作一个最好预期的答案。把从(x, y)能走到的所有格子,从大到小排好序(我的代码是从小到大排序然后从后面开始取的……),都接在当前走到(x, y)的后面,这是从(x, y)可能搜到的最好的答案,如果这个都比ans要小,那么我们也就没有必要往下搜了,果断回溯。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<cstring>
#include<queue>
#include<cmath>
#include <ctype.h>
using namespace std;
const int N = 33;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int maxn=33;
int dir[4][2]= {{-1,0},{1,0},{0,-1},{0,1}};
struct node
{
    int len, a[N];
    void clear1()
    {
        len=0;
    }
    void print()
    {
        for(int i=0; i<len; i++) printf("%d",a[i]);
        printf("\n");
    }
    bool operator <(const node& A)const
    {
        if(len!=A.len) return len<A.len;
        for(int i=0; i<len; i++)
        {
            if(a[i]!=A.a[i]) return a[i]<A.a[i];
        }
        return false;
    }
};

char str[31][31];
node ans,now;
int vis1[N][N], vis2[N][N], can[N];


int maxlen(int x,int y)
{
    int cnt=1;
    memset(vis2,0,sizeof(vis2));
    can[0]=(str[x][y]-'0');
    vis2[x][y]=1;
    queue<int>q;
    q.push((x*maxn+y));
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        x=u/maxn,y=u%maxn;
        for(int i=0; i<4; i++)
        {
            int ax=x+dir[i][0], ay=y+dir[i][1];
            if(!isdigit(str[ax][ay]) || vis1[ax][ay] || vis2[ax][ay]) continue;
            can[cnt++]=(str[ax][ay]-'0');
            q.push(ax*maxn+ay);
            vis2[ax][ay]=1;
        }
    }
    return cnt;
}

void dfs(int x,int y)
{
    vis1[x][y]=1;
    now.a[now.len++]=(str[x][y]-'0');
    for(int i=0; i<4; i++)
    {
        int ax=x+dir[i][0], ay=y+dir[i][1];
        if(!isdigit(str[ax][ay]) || vis1[ax][ay]) continue;
        int k=maxlen(ax,ay);
        if(now.len+k<ans.len) continue;
        if(now.len+k==ans.len)
        {
            sort(can,can+k);
            node tmp=now;
            for(int j=k-1; j>=0; j--) tmp.a[tmp.len++]=can[j];
            if(tmp<ans) continue;
        }
        dfs(ax,ay);
    }
    vis1[x][y]=0;
    if(ans<now) ans=now;
    now.len--;
    return ;
}




int main()
{
    int n, m;
    while(scanf("%d %d", &n, &m),(n!=0&&m!=0))
    {
        ans.clear1(), now.clear1();
        memset(str,0,sizeof(str));
        for(int i=1; i<=n; i++) scanf("%s",&str[i][1]);
        memset(vis1,0,sizeof(vis1));

        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
                if(isdigit(str[i][j])) dfs(i,j);

        ans.print();
    }
    return 0;
}

更高效


#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cmath>
#include <bits/stdc++.h>
using namespace std;
const int N = 50;
typedef long long LL;
const int inf = 0x3f3f3f3f;
int dir[4][2]= {{-1,0},{1,0},{0,-1},{0,1}};
int n, m, total;
char str[N][N],g[N][N],ans[100],stack1[100];
int maxn, flag;




struct node
{
    int x,y;
} queue1[1000];
bool yes(int x,int y)
{
    return x>=0&&x<n&&y>=0&&y<m; //该点是否在地图中,是就返回true
}
int bfs1(int x,int y) //(x,y)处往后还能搜索到多少位有效数字
{
    node t;
    char g[20][20];
    for(int i=0; i<n; i++)strcpy(g[i],str[i]); //将g中的值赋为str
    int head,tail; //队列前后”指针“
    head=tail=0; //队列为空
    t.x=x,t.y=y; //其实节点,用于入队
    queue1[tail++]=t; //数组模拟队列
    while(head<tail) //队列不为空
    {
        x=queue1[head].x;
        y=queue1[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; //记录情况
            queue1[tail++]=t; //新节点入“队”
        }
    }
    return head; //该次处理的点数
}


void dfs(int x,int y,int cnt)
{
    if(maxn<cnt||(maxn==cnt&&flag==1))
    {
        stack1[cnt]='\0';
        strcpy(ans,stack1);
        maxn=cnt;
        flag=0;
    }
    int len=bfs(x,y);
    if(len+cnt-1<maxn||(len+cnt-1==maxn&&flag==-1)) return;
    for(int i=0; i<4; i++)
    {
        int ax=x+dir[i][0], ay=y+dir[i][1];
        if(ax<0||ax>=n||ay<0||ay>=m||str[ax][ay]=='#') continue;
        if(flag!=1&&maxn==total&&ans[cnt]>str[ax][ay]) continue;
        stack1[cnt]=str[ax][ay];
        str[ax][ay]='#';
        if(flag==0)
        {
            if(cnt>=maxn) flag=1;
            else if(ans[cnt]>stack1[cnt]) flag=-1;
            else if(ans[cnt]<stack1[cnt])flag=1;
            else flag=0;
            dfs(ax,ay,cnt+1);
            flag=0;
        }
        else dfs(ax,ay,cnt+1);
        str[ax][ay]=stack1[cnt];
    }
    return ;
}
int main()
{
    while(scanf("%d %d", &n, &m),n||m)
    {
        total=0,maxn=1;
        for(int i=0; i<n; i++)
        {
            scanf("%s",str[i]);
            for(int j=0; j<m; j++)  if(str[i][j]!='#') total++;
        }
        memset(ans,0,sizeof(ans));
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                if(str[i][j]=='#') continue;
                if(maxn==total&&ans[0]>str[i][j]) continue;
                stack1[0]=str[i][j];
                str[i][j]='#';
                if(ans[0]==stack1[0]) flag=0;
                else if(ans[0]<stack1[0]) flag=1;
                else flag=-1;
                dfs(i,j,1);
                str[i][j]=stack1[0];
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值