【集训】DFS/BFS专训2

DFS/BFS专训2


A. 面积

题目描述
编程计算由“ * ”号围成的下列图形的面积。面积计算方法是统计*号所围成的闭合曲线中水平线和垂 直线交点的数目。如下图所示,在 10 * 10 的二维数组中,有“ * ”围住了 15 个点,因此面积为 15。

0 0 0 0 0 0 0 0 0 0 
0 0 0 0 * * * 0 0 0 
0 0 0 0 * 0 0 * 0 0 
0 0 0 0 0 * 0 0 * 0
0 0 * 0 0 0 * 0 * 0 
0 * 0 * 0 * 0 0 * 0 
0 * 0 0 * * 0 * * 0 
0 0 * 0 0 0 0 * 0 0  
0 0 0 * * * * * 0 0 
0 0 0 0 0 0 0 0 0 0

输入格式 一个矩阵

输出格式 面积

样例输入

0 0 0 0 0 0 0 0 0 0 
0 0 0 0 * * * 0 0 0 
0 0 0 0 * 0 0 * 0 0 
0 0 0 0 0 * 0 0 * 0
0 0 * 0 0 0 * 0 * 0 
0 * 0 * 0 * 0 0 * 0 
0 * 0 0 * * 0 * * 0 
0 0 * 0 0 0 0 * 0 0  
0 0 0 * * * * * 0 0 
0 0 0 0 0 0 0 0 0 0

样例输出

15

代码
从四边向内进行宽搜 再用总面积减去未被围起来的面积即可

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;

int a[50][50];
bool v[50][50];
int n = 10, cnt;
char c;
int dx[4] = { 0, 1, 0, -1 };
int dy[4] = { 1, 0, -1, 0 };

struct node {
    int x;
    int y;
};
queue<node> q;

void bfs() {
    while (!q.empty()) {
        node f = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            node t;
            t.x = f.x + dx[i];
            t.y = f.y + dy[i];
            if (t.x >= 1 && t.x <= n && t.y >= 1 && t.y <= n && !a[t.x][t.y] && !v[t.x][t.y]) {
                v[t.x][t.y] = 1;
                a[t.x][t.y] = 1;
                q.push(t);
            }
        }
    }
}

int main() {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    for (int i = 1; i <= n; i++) {
        if (a[1][i] == 0) {
            a[1][i] = 1;
            v[1][i] = 1;
            q.push({ 1, i });
            break;
        }
    }
    for (int i = 1; i <= n; i++) {
        if (a[i][10] == 0) {
            a[i][10] = 1;
            v[i][10] = 1;
            q.push({ i, 10 });
            break;
        }
    }
    for (int i = 1; i <= n; i++) {
        if (a[i][1] == 0) {
            a[i][1] = 1;
            v[i][1] = 1;
            q.push({ i, 1 });
            break;
        }
    }
    for (int i = 1; i <= n; i++) {
        if (a[10][i] == 0) {
            a[10][i] = 1;
            v[10][i] = 1;
            q.push({ 10, i });
            break;
        }
    }
    bfs();
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (a[i][j] == 0)
                cnt++;
        }
    }
    printf("%d", cnt);
}

B. 营救

题目描述
铁塔尼号遇险了!他发出了求救信号。距离最近的哥伦比亚号收到了讯息,时间就是生命,必须尽快 赶到那里。
通过侦测,哥伦比亚号获取了一张海洋图。这张图将海洋部分分化成 n*n 个比较小的单位,其中用 1 标明的是陆地,用 0 标明是海洋。船只能从一个格子,移到相邻的四个格子。
为了尽快赶到出事地点,哥伦比亚号最少需要走多远的距离。

输入格式
第一行为 n,下面是一个 n*n 的 0、1 矩阵,表示海洋地图。
最后一行为四个小于 n 的整数,分别表示哥伦比亚号和铁塔尼号的位置。

输出格式
哥伦比亚号到铁塔尼号的最短距离,答案精确到整数。

样例输入

3
001
101
100
1 1 3 3

样例输出

4

代码
宽搜水题

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;

struct node {
    int x, y, dis;
};
queue<node> q;
int n, stx, sty, edx, edy;
string s;
int a[1010][1010], ans[1010][1010];
bool v[1010][1010];
int dx[4] = { 0, 1, 0, -1 };
int dy[4] = { 1, 0, -1, 0 };

void bfs(int x, int y) {
    while (!q.empty()) {
        node f = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            node t;
            t.x = f.x + dx[i];
            t.y = f.y + dy[i];
            if (t.x > 0 && t.y > 0 && t.x <= n && t.y <= n && a[t.x][t.y] == 0 && v[t.x][t.y] == 0) {
                //	printf("%d %d\n",t.x ,t.y);
                v[t.x][t.y] = 1;
                t.dis = f.dis + 1;
                ans[t.x][t.y] = t.dis;
                q.push(t);
                if (t.x == edx && t.y == edy) {
                    printf("%d\n", t.dis);
                    break;
                }
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        cin >> s;
        for (int j = 1; j <= n; j++) {
            a[i][j] = s[j - 1] - '0';
        }
    }

    scanf("%d%d%d%d", &stx, &sty, &edx, &edy);
    node st;
    st.x = stx;
    st.y = sty;
    st.dis = 0;
    v[stx][sty] = 1;
    q.push(st);
    bfs(stx, sty);
    //	for(int i=1;i<=n;i++){
    //		for(int j=1;j<=n;j++){
    //			printf("%d ",ans[i][j]);
    //		}printf("\n");
    //	}
}

C. 最少转弯问题

题目描述
给出一张地图,这张地图被分为n×m(n,m<=100)个方块,任何一个方块不是平地就是高山。平地可以通过,高山则不能。现在你处在地图的(x1,y1)这块平地,问:你至少需要拐几个弯才能到达目的地(x2,y2)?你只能沿着水平和垂直方向的平地上行进,拐弯次数就等于行进方向的改变(从水平到垂直或从垂直到水平)的次数。例如:如图,最少的拐弯次数为5。
在这里插入图片描述

输入格式

第一行:n和m。
第二至n+1行:一个矩阵,为地图。(0:空地;1:高山。)
第n+2行:x1,y1,x2和y2。

输出格式

只有一行,为最少的转弯次数。

样例输入

5 7 
1 0 0 0 0 1 0 
0 0 1 0 1 0 0 
0 0 0 0 1 0 1 
0 1 1 0 0 0 0 
0 0 0 0 1 1 0 
1 3 1 7

样例输出

5

代码

#include <iostream>
#include <cstdio>
#include <queue>
#define inf 0x7fffffff
using namespace std;

struct node {
    int x, y, dis, dir;
};
queue<node> q;
int n, m, stx, sty, edx, edy;
int a[1010][1010], ans[1010][1010];
bool v[1010][1010];
int dx[4] = { 0, 1, 0, -1 };
int dy[4] = { 1, 0, -1, 0 };
node st;

void bfs() {
    while (!q.empty()) {
        node f = q.front();
        //	printf("%d\n",f.dis );
        q.pop();
        for (int i = 0; i < 4; i++) {
            node t;
            t.dir = i;
            t.x = f.x + dx[i];
            t.y = f.y + dy[i];
            if (f.x == stx && f.y == sty)
                f.dir = i;

            if (t.x > 0 && t.y > 0 && t.x <= n && t.y <= m && a[t.x][t.y] == 0) {
                //	printf("%d %d\n",t.x ,t.y);
                t.dis = f.dis;
                if (t.dir != f.dir)
                    t.dis = f.dis + 1;
                ans[t.x][t.y] = min(ans[t.x][t.y], t.dis);

                if (v[t.x][t.y] == 0) {
                    v[t.x][t.y] = 1;
                    q.push(t);
                }
            }
        }
    }
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            scanf("%d", &a[i][j]);
            if (a[i][j] == 0)
                ans[i][j] = inf;
        }
    }
    scanf("%d%d%d%d", &stx, &sty, &edx, &edy);

    st.x = stx;
    st.y = sty;
    st.dis = 0;
    st.dir = 0;
    v[stx][sty] = 1;
    ans[stx][sty] = 0;
    q.push(st);
    bfs();
    //	for(int i=1;i<=n;i++){
    //		for(int j=1;j<=m;j++){
    //			printf("%d ",ans[i][j]);
    //		}printf("\n");
    //	}
    printf("%d", ans[edx][edy]);
}

D. 麻将游戏

题目描述 (此题巨坑无比
在一种“麻将”游戏中,游戏是在一个有w*h格子的矩形平板上进行的。每个格子可以放置一个麻将牌,也可以不放(如图所示)。玩家的目标是将平板上的所有可通过一条路径相连的两张相同的麻将牌,从平板上移去。最后如果能将所有牌移除平板,则算过关。

这个游戏中的一个关键问题是:两张牌之间是否可以被一条路径所链接,该路径满足以下两个特性:
(1)它由若干条线段组成,每条线段要么是水平方向,要么是垂直方向。
(2)这条路径不能横穿任何一个麻将牌(但允许路径暂时离开平板)
这是一个例子:
麻将游戏

在(1,3)的牌和在(4,4)的牌可以被连接。(2,3)和(3,4)不能被连接。
你的任务是编一个程序,检测两张牌是否能被一条符合以上规定的路径所连接。

输入格式
第一行有两个整数w,h(1<=w,h<=75),表示平板的宽和高。接下来h行描述平板信息,每行包含w个字符,如果某个格子有一张牌,则这个格子上有个’X’,否则是一个空格。平板上最左上角格子的坐标为(1,1),最右下角格子的坐标为(w,h)。接下来的若干行,每行有四个数x1,y1,x2,y2,且满足1<=x1,x2<=w,1<=y1,y2<=h,表示两张牌的坐标(这两张牌的坐标总是不同的)。如果出现连续四个0,则表示输入结束。

输出格式
对于每一对牌输出占一行,为连接这一对牌的路径最少包含的线段数。如果不存在路径则输出0。

样例输入
很坑

5 4 
XXXXX
X   X
XXX X
 XXX 
2 3 5 3
1 3 4 4
2 3 3 4
0 0 0 0
//坐标(y,x) 

样例输出

4
3
0

代码

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

int m,n,a[600][600];
string s;
bool v[600][600];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
struct node{
	int x,y,d;
};
node st,ed;

void bfs()
{
	memset(v,0,sizeof v);
	queue<node>q;
	st.d=0;
	q.push(st);a[ed.x][ed.y]=0;
	v[st.x ][st.y ]=1;
	while(!q.empty() )
	{
		node f=q.front(); q.pop() ;
		for(int i=0;i<4;i++)
		{
			node t;
			t.x=f.x+dx[i];
			t.y=f.y+dy[i];
			while(t.x<=m+1&&t.y<=n+1&&t.x>=0&&t.y>=0&&a[t.x][t.y]==0)
			{
			//	printf("%d %d %d\n",t.x,t.y,f.d+1);
				if(t.x==ed.x&&t.y==ed.y)
				{
					a[ed.x][ed.y]=1; 
					printf("%d\n",f.d+1);
					return;
				}
				if(v[t.x][t.y]==0)
				{
					v[t.x][t.y]=1;
					q.push({t.x,t.y,f.d+1}); 
				}
				t.x+=dx[i];
				t.y+=dy[i];
			}
		} 
	}
	printf("0\n");
	a[ed.x][ed.y]=1; 
}
 
int main(){
	//天*的输入!!!! 
	cin>>n>>m;
	getline(cin,s);
	for(int i=1;i<=m;i++)
	{
		getline(cin,s);
		for(int j=0;j<n;j++)
			if(s[j]=='X') a[i][j+1]=1;
	}
	for(int i=1;;i++)
	{
		scanf("%d%d%d%d",&st.y ,&st.x ,&ed.y ,&ed.x );//注意xy是反的
		if(st.x==0) return 0;
		bfs();
	}
	return 0;
}

注意输入!!(字符串总是会出现一些莫名其妙的问题
如果采用

scanf("%d%d\n",&m,&n);
for(int i=1;i<=n;i++{
	getline( cin, s) ;
	for(int j=0;j<m;j++)
		if(s[j]=='X') a[i][j+1]=1;
}

会出事!!!!!!!!!!
例如

5 4
 XXX
X   X
XXX X
 XXX 

‘ \n ’会滤掉回车键 包括矩阵第一行的空格!
这样读入就变成了

XXX
X   X
XXX X
 XXX 

会出大问题
调了两个小时


E. 门票问题

题目描述
有很多人在门口排队,每个人将会被发到一个有效的通行密码作为门票。一个有效的密码由 L(3≤L≤15)个小写英文字母组成,至少有一个元音(a、e、i、o 或 u)和两个辅音(除去元音以外的音节),并且是按字母表顺序出现的(例如 abc 是有效的,而 bac 不是)。

现在给定一个期望长度为L和C(1≤C≤26)个小写字母,写一个程序,输出所有的长度为L、能由所给定的 C 个字母组成的有效密码。密码必须按字母表顺序打印出来,一行一个。

输入格式
第 1 行是两个由一个空格分开的正整数,L 和 C,3≤L≤15,1≤C≤26。

第 2 行是 C 个由一个空格隔开的小写字母,密码是由这个字母集中的字母来构建的。

输出格式
若干行,每行输出一个长度为 L 个字符的密码(没有空格)。输出行必须按照字母顺序排列。

程序只需要输出前 25 000 个有效密码,即使后面还存在有效密码。

样例输入

4 6
a t c i s w

样例输出

acis
acit
aciw
acst
acsw
actw
aist
aisw
aitw
astw
cist
cisw
citw
istw

代码
半全排列 经典深搜 check比较麻烦

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

int l, c, cnt;
char a[50], t[50];

bool check(int k) {
    for (int i = 1; i < k; i++) {
        if (t[k] <= t[i])
            return 0;
    }
    return 1;
}

bool sieve(char x) {
    if (x == 'a' || x == 'e' || x == 'i' || x == 'o' || x == 'u') {
        return 1;
    }
    return 0;
}

bool check_f() {
    int cnt_f = 0, cnt_ff = 0;
    for (int i = 1; i <= l; i++) {
        if (sieve(t[i]))
            cnt_f++;
        else
            cnt_ff++;
    }
    if (cnt_f >= 1 && cnt_ff >= 2)
        return 1;
    else
        return 0;
}

void dfs(int k) {
    if (cnt >= 25000)
        return;
    if (k == l + 1) {
        if (check_f() == 0)
            return;
        for (int i = 1; i <= l; i++) {
            printf("%c", t[i]);
        }
        printf("\n");
        cnt++;
    } else {
        for (int i = 1; i <= c; i++) {
            t[k] = a[i];
            if (check(k)) {
                dfs(k + 1);
            }
        }
    }
}

int main() {
    scanf("%d%d", &l, &c);
    for (int i = 1; i <= c; i++) {
        cin >> a[i];
    }
    sort(a + 1, a + 1 + c);

    dfs(1);
}

F. 奇怪的电梯(lift)

题目描述
大楼的每一层楼都可以停电梯,而且第 i 层楼(1<=i<=N)上有一个数字 Ki(0<=Ki<=N)。 电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果 不能满足要求,相应的按钮就会失灵。例如:3 3 1 2 5 代表了 Ki(K1=3,K2=3,……),从一 楼开始。在一楼,按“上”可以到 4 楼,按“下”是不起作用的,因为没有-2 楼。那么, 从 A 楼到 B 楼至少要按几次按钮呢?

输入格式
输入文件共有二行,第一行为三个用空格隔开的正整数,表示 N,A,B(1≤N≤200, 1≤ A,B≤N),第二行为 N 个用空格隔开的正整数,表示 Ki。

输出格式
最少按键次数,若无法到达,则输出-1。

样例输入

5 1 5 
3 3 1 2 5 

样例输出

3

代码

#include <iostream>
#include <cstdio>
#define inf 100000 //设置上限
using namespace std;

int n, A, B, cnt = inf;
int a[205];
bool v[205]; //标记剪枝数组

bool check(int k, int i) {
    if (i == 0) {
        if (k + a[k] > n) //不飞冲天
            return 0;
    } else {
        if (k - a[k] < 1) //不下阴间
            return 0;
    }
    return 1;
}

void dfs(int k, int s) {
    if (k == B) {
        cnt = min(cnt, s);
    } else if (cnt >= s) {
        v[k] = 1;
        if (check(k, 0) && !v[k + a[k]]) //上行
            dfs(k + a[k], s + 1);
        if (check(k, 1) && !v[k - a[k]]) //下行
            dfs(k - a[k], s + 1);
        v[k] = 0;
    }
}

int main() {
    scanf("%d%d%d", &n, &A, &B);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }

    dfs(A, 0);
    if (cnt == inf)
        printf("-1");
    else
        printf("%d", cnt);
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值