信息新生训练 BFS,DFS 小朋友的专题作业

A,题目链接https://www.oj.swust.edu.cn/problem/show/1378
解法:DFS,经典的FloodFilld填充,找个点开始DFS,把这个点可以扩展到的点标记,然后答案加1,然后继续DFS其他没有被访问的点。

#include<stdio.h>
#include<queue>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
char G[110][110];
bool vis[110][110];
int d[][2]={{-1,0},{1,0},{0,-1},{0,1}};
void dfs(int x,int y)
{
    int i,xx,yy;
    vis[x][y]=true;
    for(i=0;i<4;i++)
    {
        xx=x+d[i][0];
        yy=y+d[i][1];
    if(xx<n&&xx>=0&&yy>=0&&yy<m&&vis[xx][yy]==false&&G[xx][yy]=='#')
    {
        dfs(xx,yy);
    }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
        scanf("%s",G[i]);
    int count=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(G[i][j]=='#'&&vis[i][j]==false)
            {
                dfs(i,j);
                count++;
            }
        }
    }
    printf("%d\n",count);
    return 0;
}

B,题目链接https://www.oj.swust.edu.cn/problem/show/1381
题意:就是求迷宫最短路,最简单的BFS,换成DFS是一样的。让我惊奇的是,大家都是写的BFS。。。我懒得改DFS,直接贴BFS了,自己稍微改一下就行了。。。

#include<stdio.h>  
#include<queue>  
using namespace std;
struct node  
{  
    int x;  
    int y;  
    int d;  
}cx;
char map[101][101];
int sx,sy,R,C;
int dir[4][2]={{0,-1},{0,1},{1,0},{-1,0}};
void bfs()  
{  
    int tx,ty,i;  
    queue<node> q;  
    node kb={sx,sy,0};  
    q.push(kb);  
    while(1)  
    {  
        cx=q.front(); 
        q.pop();
        for(i=0;i<4;i++)  
        {  
            tx=cx.x+dir[i][0];  
            ty=cx.y+dir[i][1];  
            if(tx>=0&&ty>=0&&tx<R&&ty<C&&map[tx][ty]!='*')  
            {  
                if(map[tx][ty]=='B')  
                {  
                    printf("%d\n",cx.d+1);  
                    return;  
                }  
                node ad={tx,ty,cx.d+1};              
                  q.push(ad);
                map[tx][ty]='*';  
            }  
        }  
    }  
}  
int main()  
{  
    int i,j;  
    scanf("%d%d",&R,&C);  
    for(i=0;i<R;i++)  
    {  
        scanf("%s",map[i]);  
        for(j=0;j<C;j++)  
            if(map[i][j]=='C')  
            {  
                sx=i;  
                sy=j;  
            }  
    }  
    map[sx][sy]='*';  
    bfs();  
    return 0;  
}  

题目链接:https://www.oj.swust.edu.cn/problem/show/1112
解法: 简单DP加高精度,dp[i][j]代表第i个人取j课花生的方案数。然后谁便转转就好了,至于高精度已经讲过了。

#include<bits/stdc++.h>
using namespace std;
#define N 3003
#define L 10003
string add(string a,string b)//只限两个非负整数相加
{
    string ans;
    int na[L]={0},nb[L]={0};
    int la=a.size(),lb=b.size();
    for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
    for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    for(int i=0;i<lmax;i++) na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10;
    if(na[lmax]) lmax++;
    for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
    return ans;
}
string dp[2][N];
int main()
{
    int n;
    while(~scanf("%d",&n)){
        for(int i=0;i<=n;i++){
            for(int j=0;j<2;j++) dp[j][i]="0";
        }
        dp[0][1]=dp[0][2]="1";
        for(int i=2;i<=n;i++){
            if(i%2){
                dp[0][i]=add(dp[1][i-1],dp[1][i-2]);
                dp[0][i+1]=add(dp[1][i],dp[1][i-1]);
            }
            else{
                dp[1][i]=add(dp[0][i-1],dp[0][i-2]);
                dp[1][i+1]=add(dp[0][i],dp[0][i-1]);
            }
        }
        cout<<dp[0][n]<<endl;
    }
    return 0;
}

D,题目链接:https://www.oj.swust.edu.cn/problem/show/1400

解法:康拓展开加BFS。康拓展开可以看http://blog.csdn.net/acdreamers/article/details/7982067
因为BFS要判断是否是否被标记过,那么这里我们开9维每维都是10就是vis[10]…[10],显然空间会炸的,所以我们用康拓展开求得每个状态一个特定的值的话,就可以只开一维了,空间就可以开下了,其实康拓展开也是一种Hash,其实也不一定非得康拓展开,你手写Hash函数,只要保证每个合法状态的Hash值唯一,这题就做完了。其实很简单,就是代码稍微长一点。我比较懒没写,代码用的学弟的,将就看。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};//阶乘表
int dir[4][2]={1,0,0,1,-1,0,0,-1};//方向
int vis[362881];
int kangst,kanged;
int t1[3][3];
int t2[3][3];
pair<int,int>p;
struct node{
    int maze[3][3];
    pair<int,int>pos;
    int kang;
    int step;
};
int  Kang_open (int t[3][3])
{
    int s[9],num=0,k=0,sum=0;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            s[k++]=t[i][j];
    for(int i=0;i<9;i++){
        num=0;
        for(int j=i+1;j<9;j++)
        {
            if(s[i]>s[j])
                num++;
        }
        sum+=num*fac[8-i];
    }
    return sum;
}
int bfs()
{
    node now;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            now.maze[i][j]=t1[i][j];
    now.kang=kangst;
    now.step=0;
    now.pos=p;
    vis[kangst]=1;
    queue<node>que;
    que.push(now);
    while(!que.empty())
    {
        now=que.front();
        que.pop();
        if(now.kang==kanged)
            return now.step;
        node next=now;
        for(int i=0;i<4;i++)
        {
            next=now;
            next.pos.first = now.pos.first+dir[i][0];
            next.pos.second= now.pos.second+dir[i][1];
            if(next.pos.first >=0&&next.pos.first <3&&next.pos.second>=0&&next.pos.second<3)
            {
                next.maze[now.pos.first][now.pos.second]=now.maze[next.pos.first][next.pos.second];
                next.maze[next.pos.first][next.pos.second]=0;
                next.kang=Kang_open(next.maze);
                if(!vis[next.kang])
                {
                    vis[next.kang]=1;
                    next.step++;
                    que.push(next);
                }
            }
        }
    }
    return -1;
}
int main ()
{
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++){
            scanf("%d",&t1[i][j]);
            if(t1[i][j]==0)
                p.first=i,p.second=j;
        }
    kangst=Kang_open(t1);
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            scanf("%d",&t2[i][j]);
    kanged=Kang_open(t2);
    printf("%d\n",bfs());
    return 0;
}

E,题目链接https://www.oj.swust.edu.cn/problem/show/1319
题意:仍然是求一个迷宫最短路,但是这个迷宫里面的机器人是有一个方向的,然后他可以接受一些指令,是向左转,向右转,或者直走,问最短路的值,或者不能到达输出-1
解法:仍然BFS,但是我们记录一个当前的方向,相应的标记数组f改成3维数组标记

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
char g[105][105];
bool f[105][105][5];
int d[4][2] = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } };
struct node
{
    int s, x, y, dx;
};
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n, m;
        cin >> n >> m;
        int i, j;
        int x, y;
        for (i = 1; i <= n;i++)
        for (j = 1; j <= m; j++)
        {
            cin >> g[i][j];
            if (g[i][j] == 'S')
            {
                x = i;
                y = j;
            }
        }
        node k;
        k.x = x;
        k.y = y;
        k.s = 0;
        k.dx = 0;
        memset(f, 0, sizeof(f));
        f[x][y][0] = 1;
        queue<node>q;
        int ans=-1;
        q.push(k);
        while (!q.empty())
        {
            k = q.front();
            q.pop();
            if (g[k.x][k.y] == 'T')
            {
                ans=k.s;
                break;
            }
            node p;
            p = k;
            p.x = k.x + d[k.dx][0];
            p.y = k.y + d[k.dx][1];
            p.s++;
            if (1 <= p.x&&p.x <= n && 1 <= p.y&&p.y <= m&&g[p.x][p.y] != '#'&&!f[p.x][p.y][p.dx])
            {
                f[p.x][p.y][p.dx] = 1;
                q.push(p);
            }
            p = k;
            p.s++;
            for (i = -1; i <= 1;i++)
            if (i)
            {
                p.dx = (k.dx + 4 + i) % 4;
                if (!f[p.x][p.y][p.dx])
                {
                    f[p.x][p.y][p.dx] = 1;
                    q.push(p);
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

G,题目链接:https://www.oj.swust.edu.cn/problem/show/2360
题意:给了2个4位数的密码,第一个密码可以执行两种操作,一种是把一个数字加1,一个是把一个数字-1,如果当前数字是9的话,加1变成1,当前数字是1的话,减1变成9,你想象你的密码箱的锁,问你最少执行多少步操作可以正确打开这个密码锁。
解法:DFS回溯法,状态最多有9*9*9*9,所以大力DFS,BFS也可以,就是代码稍微长一点。

#include<bits/stdc++.h>
using namespace std;
int n[15],m[15];
bool vis[15];
char s[15];
int t,stp;
int check(int num)
{
    int x[10]={0,num/1000,num%1000/100,num%100/10,num%10};
    int p=0;
    for(int i = 1;i <= 4;i++)
    {
        p+=min(fabs(m[i]-x[i]),9-fabs(m[i]-x[i]));
    }
    return p;
}
void DFS(int num,int ex)
{
    if(num>=1000)
    {
        stp=min(stp,check(num)+ex);
    }
    else
    {
        for(int i = 1;i <= 4;i++)
        {
            if(!vis[i])
            {
                vis[i] = 1;
                DFS(num*10+n[i],ex++);
                vis[i] = 0;
            }
        }
    }
}
int main ()
{
    scanf("%d",&t);
    while(t--)
    {
        stp=1e5;
        scanf("%s",s+1);
        for(int i = 1;i <= 4;i++)
            n[i] = s[i]-'0';
        scanf("%s",s+1);
        for(int i = 1;i <= 4;i++)
            m[i] = s[i]-'0';
        DFS(0,0);
        printf("%d\n",stp);
    }
    return 0;
}

I,题目链接:https://www.oj.swust.edu.cn/problem/show/1973
题意:很多的蚂蚁都在长度为L(cm)的膀子上爬行,它们的速度都是1cm/s,到了棒子终端的时候,蚂蚁就会掉下去。如果在爬行途中遇到其他蚂蚁,两只蚂蚁的方向都会逆转。已知蚂蚁在棒子的最初位置坐标,但是我们不知道他们会往哪一个方向爬。请求出所有蚂蚁掉下去的最短时间和最长时间。

解法:
由题意可知,蚂蚁数的上限为2^1000000,所以直接枚举肯定不行。

其实换一个角度就可以想出来,蚂蚁的个体是相同的,所以相遇和没相遇一个水平,所以要么从一头掉下,只要找出所有蚂蚁与较远端比较,然后找出最大值就是所需要的最大时间在距离短的一边中求出最大的一边。就能找到距离端点最远的蚂蚁需要爬到端点的时间。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int main ()
{
    int t;scanf("%d",&t);
    int l,ants,Max,Min,ant;
    while(t--){
        Max=0,Min=0;
        scanf("%d%d",&l,&ants);
        for(int i=0;i<ants;i++){
            scanf("%d",&ant);
            Max=max(Max,max(ant,l-ant));//最远
            Min=max(Min,min(ant,l-ant));//最近的最远
        }
        printf("%d %d\n",Min,Max);
    }
    return 0;
}

J,题目链接:https://www.oj.swust.edu.cn/problem/show/1613
解法:找规律。。。很简单不说了。。

#include<cstdio>
char a[3]={'1','4','7'};
int main ()
{
    int n;
    while(~scanf("%d",&n))
    {
        if(n<=3){printf("%c\n",a[n-1]);continue;}
        int b[1000],t=0;
        n--;
        while(n/3)
        {
            b[t++]=n%3;
            n=n/3-1;
        }
        b[t]=n;
        while(t>=0){printf("%c",a[b[t--]]);}
        printf("\n");
    }
}

K,题目链接https://www.oj.swust.edu.cn/problem/show/1873
题意:

给定两个四位素数a b,要求把a变换到b

变换的过程要保证 每次变换出来的数都是一个 四位素数,而且当前这步的变换所得的素数 与 前一步得到的

素数 只能有一个位不同,而且每步得到的素数都不能重复。

求从a到b最少需要的变换次数。无法变换则输出Impossible

解法:

超级水题,40入口的BFS + 素数判定

不过剪枝之后就没有40入口了,入口数远小于40

无论是判定素数还是搜索素数,首先排除偶数,这样就剪掉一半枝叶了

判断素数用根号法判断,

如果一个数X不能被 [2,√X] 内的所有素数整除,那么它就是素数

可以判断的复杂度降到logn

注意:千位的变换要保证千位不为0,其实素数也是用来辅助搜索剪枝的

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

int getInt(int &x)
{
    return scanf("%d",&x);
}
int is[10010];
int ans[10010];
bool vis[10010];
void init()
{
    memset(is,0,sizeof(is));
    is[1]=1;
    for(int i=2;i*i<=10010;i++)
    {
        if(!is[i])
        {
            for(int j=i*i;j<=10010;j+=i)
            {
                is[j]=1;
            }
        }
    }
}

int bfs(int a,int b)
{
    queue<int>qu;
    qu.push(a);
    ans[a]=0;
    vis[a]=true;
    while(!qu.empty())
    {
        int tmp=qu.front();
        qu.pop();
        for(int i=0;i<=9;i++)
        {
            int tmp1=(tmp/10)*10+i;//个位
            if(!is[tmp1]&&!vis[tmp1])
            {
                qu.push(tmp1);
                ans[tmp1]=ans[tmp]+1;
                vis[tmp1]=true;
            }
            int tmp2=(tmp/100)*100+tmp%10+i*10;//十位
            if(!is[tmp2]&&!vis[tmp2])
            {
                qu.push(tmp2);
                ans[tmp2]=ans[tmp]+1;
                vis[tmp2]=true;
            }
            int tmp3=(tmp/1000)*1000+tmp%100+i*100;//百位
            if(!is[tmp3]&&!vis[tmp3])
            {
                qu.push(tmp3);
                ans[tmp3]=ans[tmp]+1;
                vis[tmp3]=true;
            }
            if(i!=0)
            {
                  int tmp4=tmp%1000+i*1000;//千位,无前导0
                  if(!is[tmp4]&&!vis[tmp4])
                  {
                      qu.push(tmp4);
                      ans[tmp4]=ans[tmp]+1;
                      vis[tmp4]=true;
                  }
            }
            if(vis[b])return ans[b];
        }
    }
    return -1;
}
int main()
{
    int n,a,b;
    init();
    getInt(n);
    while(n--)
    {
        getInt(a);getInt(b);
        memset(ans,0,sizeof(ans));
        memset(vis,false,sizeof(vis));
        int ans=bfs(a,b);
        if(ans==-1)
            puts("Impossible");
        else
            printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值