2022.12.27(总结)

一,计算池塘的个数

题目:

由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。我们用一个 N\times M(1\leq N\leq 100, 1\leq M\leq 100)N×M(1≤N≤100,1≤M≤100) 的网格图表示。每个网格中有水(W) 或是旱地(.)。一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。约翰想弄清楚他的田地已经形成了多少水坑。给出约翰田地的示意图,确定当中有多少水坑。

输入第 11 行:两个空格隔开的整数:NN 和 MM。

第 22 行到第 N+1N+1 行:每行 MM 个字符,每个字符是 W 或 .,它们表示网格图中的一排。字符之间没有空格。

输出一行,表示水坑的数量。

输入输出样例

输入 #1复制

10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.

输出 #1复制

3

分析:

1,首先,我们要找水坑就要找田地中的'W',所以在我们要构建一个双重循环找'W'。

2,在找到'W'之后,要把这一片的水域都消掉,算成一个池塘。

3,消去这片水域的过程需要使用深搜,向八个方向搜索,在不越界且是'W'的情况下,将这个水坑消掉也就是赋'.'。

 4,再在找到的'W'的位置上向八个方向找,依次递归,直至将这片水域消掉。

代码如下:

#include<stdio.h>
#include<math.h>
#include<string.h>
 int n,m,sum;
 char a[101][101];
int x1[8]={-1,1,0,0,-1,-1,1,1};
int kd[8]={0,0,-1,1,-1,1,-1,1};
void ss(int x,int y)
{
    a[x][y]='.';
    for(int i=0;i<8;i++)
    {
        if((x+x1[i]>=0&&x+x1[i]<n)&&(y+kd[i]>=0&&y+kd[i]<m)&&a[x+x1[i]][y+kd[i]]=='W')
        {
            ss(x+x1[i],y+kd[i]);
        }
    }
}

void zhao()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(a[i][j]=='W')
            {
                sum++;
                ss(i,j);
            }
        }
    }
}

int main()
{
    scanf("%d%d\n",&n,&m);
    for(int i=0;i<n;i++)
    {
        gets(a[i]);
    }
    zhao();
    printf("%d",sum);
}

二,填涂颜色

题目描述

由数字 0 组成的方阵中,有一任意形状闭合圈,闭合圈由数字 1 构成,围圈时只走上下左右 4 个方向。现要求把闭合圈内的所有空间都填写成 2。例如:6×6 的方阵(n=6),涂色前和涂色后的方阵如下:

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

输入格式

每组测试数据第一行一个整数 n(1 \le n \le 30)n(1≤n≤30)。

接下来 n 行,由 0 和 1 组成的 n×n 的方阵。

方阵内只有一个闭合圈,圈内至少有一个 0。

输出格式

已经填好数字 2 的完整方阵。

输入输出样例

输入 #1复制

6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1

输出 #1复制

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

说明/提示

对于 100% 的数据,1≤n≤30。

分析:

1,对于这个题我们需要找到闭合圈,再找到里面的0,我们不妨逆向思维思考一下,既然我们要找的是闭合圈里面的0,那我们就把不是闭合圈里面的0赋值为2。

2,理清了这个思路,我们就在四条边上开始找0,为什么是在四条边找0呢?因为如果它的闭合圆不是整个方阵,那它的边上就一定会有0。

3,只要在边上找到一个0,顺着这个0的上下左右进行深搜,只要找到0就赋值2。

4,输出的时候如果值为2就输出0,如果值为1 就输出1,如果值为0就输出2。

注意:我一开始是想用深搜先找到闭合圆,再找那个闭合圆里面的0,再赋值为2;但是我发现找到了闭合圆后不好找到闭合圆里面的0。

例如:

000000
01111
0101
110001
100001
111111

将上面的格子变成下面这样。

222222
221111
2101
110001
100001
111111

代码如下:


#include<stdio.h>
#include<math.h>
#include<string.h>
int a[32][32],n,xc,yc;
int k[4]={-1,1,0,0},c[4]={0,0,-1,1};
void ss(int x,int y)
{
    a[x][y]=2;
    for(int i=0;i<4;i++)
    {
        if((a[x+k[i]][y+c[i]]==0)&&(x+k[i]>=0&&x+k[i]<n)&&(y+c[i]>=0&&y+c[i]<n))
        ss(x+k[i],y+c[i]);
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
   for(int i=0;i<n;i++)
   {
    if(a[0][i]==0)
    {ss(0,i);}
   }
   for(int i=0;i<n;i++)
   {
    if(a[i][0]==0)
    {
        ss(i,0);
       
    }
   }
   for(int i=0;i<n;i++)
   {
    if(a[n-1][i]==0)
    {
        ss(n-1,i);
      
    }
   }
   for(int i=0;i<n;i++)
   {
    if(a[i][n-1]==0)
    {
        ss(i,n-1);
       
    }
   }
   for(int i=0;i<n;i++)
   {
    for(int j=0;j<n;j++)
    {
        if(a[i][j]==2)
        printf("0 ");
        else if(a[i][j]==1)
        printf("1 ");
        else 
        printf("2 ");
    }
    printf("\n");
   }
}

三,考前抱佛脚

题目背景

kkksc03 的大学生活非常的颓废,平时根本不学习。但是,临近期末考试,他必须要开始抱佛脚,以求不挂科。

题目描述

这次期末考试,kkksc03 需要考 44 科。因此要开始刷习题集,每科都有一个习题集,分别有 s_1,s_2,s_3,s_4s1​,s2​,s3​,s4​ 道题目,完成每道题目需要一些时间,可能不等(A_1,A_2,\ldots,A_{s_1}A1​,A2​,…,As1​​,B_1,B_2,\ldots,B_{s_2}B1​,B2​,…,Bs2​​,C_1,C_2,\ldots,C_{s_3}C1​,C2​,…,Cs3​​,D_1,D_2,\ldots,D_{s_4}D1​,D2​,…,Ds4​​)。

kkksc03 有一个能力,他的左右两个大脑可以同时计算 22 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。

由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。

输入格式

本题包含 55 行数据:第 11 行,为四个正整数 s_1,s_2,s_3,s_4s1​,s2​,s3​,s4​。

第 22 行,为 A_1,A_2,\ldots,A_{s_1}A1​,A2​,…,As1​​ 共 s_1s1​ 个数,表示第一科习题集每道题目所消耗的时间。

第 33 行,为 B_1,B_2,\ldots,B_{s_2}B1​,B2​,…,Bs2​​ 共 s_2s2​ 个数。

第 44 行,为 C_1,C_2,\ldots,C_{s_3}C1​,C2​,…,Cs3​​ 共 s_3s3​ 个数。

第 55 行,为 D_1,D_2,\ldots,D_{s_4}D1​,D2​,…,Ds4​​ 共 s_4s4​ 个数,意思均同上。

输出格式

输出一行,为复习完毕最短时间。

输入输出样例

输入 #1复制

1 2 1 3		
5
4 3
6
2 4 3

输出 #1复制

20

分析:

1,首先我们要明白题意,同一科目同时可以做两道题是说当它做完两道题中的任意一道时接着做这个科目的另外一个题目。

2,但是如果它只有一道题,那这道题所花的时间就是这门科目的总时间,如果它只有两道题那这两道题里面花费时间最多的那道题的时间就是总时间。

3,所以它其实是求最优解,最优解可以用动态规划求,但是在数据不多的情况下可以暴力枚举然后比较求最优解,因为这一块主要是练习搜索,所以我使用的是暴力枚举,使用深搜将每种情况都列举出来然后求时间最少的。

4,一共三个参数,相对科目的习题总量,写习题的时间,第几个习题。终止条件是当我写了的习题数等于这个科目的习题总数,在递归中分为左脑和右脑两个部分,遍历完所有的搭配,在终止条件的if语句中,首先比较左脑和右脑所花的时间谁多谁少,找出多的那个,再跟之前的找出的这个科目的所花时间最少的那个值比较,找出两个之间最小的,依次递归,直至遍历完所有的可能。

代码如下:


#include<stdio.h>
#include<math.h>
#include<string.h>
int s1,s2,s3,s4,sum;
int sz[4][22];
int bj,l,r;
void ss(int s,int sz[22],int k)
{
    if(s==k)
    {int max;
    max=l>r?l:r;
    bj=max<bj?max:bj;
    return;
    }
    l+=sz[k];
    ss(s,sz,k+1);
    l-=sz[k];
    r+=sz[k];
    ss(s,sz,k+1);
    r-=sz[k];
}

int main()
{
    scanf("%d%d%d%d",&s1,&s2,&s3,&s4);
    for(int i=0;i<s1;i++)
    scanf("%d",&sz[0][i]);
    bj=9999;
    l=r=0;
    ss(s1,sz[0],0);
    sum+=bj;
    for(int i=0;i<s2;i++)
    scanf("%d",&sz[1][i]);
    bj=9999;
    l=r=0;
    ss(s2,sz[1],0);
    sum+=bj;
    for(int i=0;i<s3;i++)
    scanf("%d",&sz[2][i]);
    bj=9999;
    l=r=0;
    ss(s3,sz[2],0);
    sum+=bj;
    for(int i=0;i<s4;i++)
    scanf("%d",&sz[3][i]);
    bj=9999;
    l=r=0;
    ss(s4,sz[3],0);
    sum+=bj;
    printf("%d",sum);
}

总结:

1,深搜主要是递归,递归要明白递归函数的参数和返回值、终止条件、递归的逻辑设计,参数用于终止条件或者计算,返回值是用于返回给上一级,终止条件是用于结束此次递归的,让这个递归实现它的作用这就是递归的逻辑设计。

2,与深搜密切关联的是回溯,什么时候用回溯呢?那便是题目中有多种可能的时候,或者是求最优解的时候,我简称它为暴力枚举。

3,不用枚举的情况是,值不需要改变或者改变了之后不用再将值变回来的。

4,深搜作为搜索的一种它的搜索方法是一条路走到黑,走到没有路再走的时候再返回上一级,再向别的方向搜索。

5,深搜模板:


void  dfs(int  step)//当前状态
{
	//判断边界条件
	if(xxx){
		return;//返回上一步
	}
	//尝试每一种可能
	for(xxx){
		//判断是否符合条件
		if(xxx){
            ( //回溯)
			//继续走下一步
			dfs(step+1);
           (//回溯)
		}
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值