算法笔记-啊哈!算法!-第三章(枚举)

第三章, 枚举 (暴力破解)

枚举, 说白了就是, 穷举每一种可能.
对于我们人来说, 这个太难了.
但是对于计算机来说这种工作再适合不过了.

第一个🌰:
小试牛刀之解出以下等式:
x3 * 6528 = 3x * 8256

// 枚举, 尝试每种可能性
for (i = 1; i <= 9;  i++) {
    if ((i * 10 +3 ) * 6528 == (3*10 + i) * 8256) {
        printf("%d", i);
    }
}

第二个🌰:
算出以下等式:
xxx(数字1)+xxx(数字2)=xxx;

要求:
1.每一位的数字范围在[1, 9], 其中每个数字不能重复, 
2.xxx(数字1)+xxx(数字2)=xxx; 或者 xxx(数字2)+xxx(数字1)=xxx;  这算是一种 , 所以算出总数后需要 除以 2.

这道题, 使用标记法. 定义book数组来标记, 哪些个数字已经使用过了.

参考代码

#include <stdio.h>
int main()
{
    int a[10]; 
    int book[10]; // 标记你哪些
    int i
    int sum; // 记录
    int total = 0; // 记录总数

    // 初始化数组
    for(i =  1; i <= 9; i++)
    {
        a[i] = i;
    }

    sum = 1;
    for(a[1] = 1;  a[1] <= 9; a[1]++) {
      for(a[2] = 1;  a[2] <= 9; a[2]++) {
        for(a[3] = 1;  a[3] <= 9; a[3]++) {
          for(a[4] = 1;  a[4] <= 9; a[4]++) {
            for(a[5] = 1;  a[5] <= 9; a[5]++) {
              for(a[6] = 1;  a[6] <= 9; a[6]++) {
                for(a[7] = 1;  a[7] <= 9; a[7]++) {
                  for(a[8] = 1;  a[8] <= 9; a[8]++) {
                    for(a[9] = 1;  a[9] <= 9; a[9]++) {
                      // 初始化book数组 
                      for (i = 1; i <= 9; i++) 
                        book[i] =  0;

                      // 如果某个数出现过就标记以下
                      for (i =1; i <= 9; i++)
                        book[a[i]] = 1;

                      //  统计共出现 了多少个不同的数
                      sum = 0;
                      for (i = 1; i <= 9; i++) 
                        sum += book[i];
                      
                      // 如果同时满足两个条件:
                      // 1. 出现9个不同的数
                      // 2. 满足等式要求
                      if (sum == 9 && (a[1]*100+a[2]*10+a[3]  + a[4]*100+a[5]*10+a[6]  == a[7]*100+a[8]*10+a[9])) {
                          printf("%d %d %d %d %d %d %d %d %d",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);
                          total++; // 统计个数
                       }
                    }
                  }
                }
              }
            }
          }
        }     
      }
    }

    printf("total: %d\n", total/2); // 因为加法的交换律, a+b=c 等价于 b+a=c,  需要除以2

    getchar();
    return 0;
  }

第三个🌰:
炸弹人游戏
放置一颗威力巨大的炸弹, 尽可能多的消灭敌人.

模拟地图如下:


地图.png

在地图上有三种地图类型,
1.空白地, 使用 ‘ . ’
2.墙, 使用 ‘#’
3.敌人, 使用 ‘G’

核心代码:

// 1.首先判断地图上是否是空地, 是空地才能安放炸弹
if (a[x][y] == '.') {

// 2.然后在四个方向上, 统计消灭敌人数量
// 向下移动, 统计消灭敌人数量
while(a[x][y] != '#') 
{
  if (a[x][y] == 'G') {
    sum++;
  }
  x++; //向下移动
}

// 向上移动, 统计消灭敌人数量
while(a[x][y] != '#') 
{
  if (a[x][y] == 'G') {
    sum++;
  }
  x--; //向上移动
}

// 向左移动, 统计消灭敌人数量
while(a[x][y] != '#') 
{
  if (a[x][y] == 'G') {
    sum++;
  }
  y--; //向上移动
}

// 向右边移动, 统计消灭敌人数量
while(a[x][y] != '#') 
{
  if (a[x][y] == 'G') {
    sum++;
  }
  y++; //向上移动
}

}

完整代码:

#include <stdio.h>
int main()
{
  char map[20][21]; // 设置地图数组大小
  int sum, q, p, i, j,  x, y, n, m, max = 0;

  scanf("%d %d", &n, &m); // 输入地图大小, 20 * 20

  // 输入地图数据
  for(i = 0 ; i <= n-1; i++) {
    scanf("%s", &a[i]);
  }

  // 枚举每个地图上的点, 进行判断
  for (i = 0; i <= n-1; i++) 
  {
    for (j = 0; j <= m-1; j++) 
    {
      x = i; y = j;
      if (a[x][y] == '.') // 判断是否是空地
      {
        sum = 0; // 重新计数
        // 向下统计
        while (a[x][y] != '#') 
        {
          if (a[x][y] == 'G') 
          {
            sum++;
          } 
          x++;
        }

        // 向上统计
        while (a[x][y] != '#') 
        {
          if (a[x][y] == 'G') 
          {
            sum++;
          } 
          x--;
        }

        // 向左统计
        while (a[x][y] != '#') 
        {
          if (a[x][y] == 'G') 
          {
            sum++;
          } 
          y++;
        }

        // 向右统计
        while (a[x][y] != '#') 
        {
          if (a[x][y] == 'G') 
          {
            sum++;
          } 
          x++;
        }

        // 四个方向统计结束后, 进行判断是否是最大消灭敌人的位置
        if (sum > max)  
        {
          max = sum;
          p = x;
          q = y;
        }
      }
    }
  }

  printf("炸弹放置在坐标(%d, %d), 最大消灭敌人个数 %d\n", p, q, max);
  
  getchar();
  return 0;
}

第四个🌰:
火柴棍等式
现在有n (n <= 24)根火柴棍, 希望能拼出等式: A + B = C的等式.
其中A, B, C都是用火柴棍拼出来的整数(非零, 则最高位不能是0),
数组0~9, 拼法如下:

火柴棍拼法.png

定义数组如下:

int f[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6}; // 用来表示每个数所需要的火柴棍的数量

关键点:

  1. 符号+, = 需要4跟火柴棍. 现在一共有24, 这样话, 减去4根, 还剩下20根.
  2. 在0~9, 10个数中, 需要最少火柴棍的就是数字 1 , 只需要2根. 所以, 最大能摆出的数字就是 11111
  3. A, B, C三个数, 只需要算出A, B,就能算出C, 这样就能算出所需的火柴棍数量.

参考代码如下:

// 算出x需要多少根火柴棍的方法
int func(int  x)
{
  int i = 0, sum = 0;
  int f[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
  
  while(x != 0) 
  {
    sum += f[x%10];
    x = x/10;
  }

  return sum;
}

int main()
{
  int a, b, c;
  int m;
  int sum =0; // 统计等式的个数

  scanf("%d", &m); // 输入火柴棍的个数  

  // 开始枚举a, b
  for(a = 0; a <= 11111; a++) 
  {
    for(b = 0; b <= 11111; b++)
    {
      c = a+b; // 通过a+b,  计算出c
      if (f(c) + f(b) + f(a) == m-4) // 判断三个数字所用的火柴棍的总数是否等于m-4
      {
        printf("%d + %d = %d\n", a, b, c);
        sum++;
      }
    }
  }

  printf("一共有 %d 不同的等式\n", sum);

  getchar();
  return 0;
}

第5五个🌰:
数的全排列

三个数, 1,2,3的全排列是:
123, 132, 213, 231, 312, 321, 六个数.
四个数, 五个数依次类推,

我们可以通过三个for循环来求的结果
参考代码

for(a = 1; a  <= 3; a++) 
{
  for (b = 1; b <= 3; b++) 
  {
    for (c = 1; c <= 3; c++) 
    {
      if (a !=b && a != c && b != c) 
      {
        printf("%d %d %d\n", a, b, c);
      }
    }
  }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。