【2024蓝桥杯/C++/A组/五子棋对弈】

题目

思路

完全模拟下棋,既没必要也不应该,因为终局一样就是同一个情况,不应该重复计数

坐标映射看这个:关于坐标压缩式子的解压缩式子-CSDN博客

注意这种错误,走分支直接上手修改数据,发现分支不合理竟然不是恢复再走其它分支,而是结束遍历。结果没回溯,且少遍历分支

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 6;

int n = 5;
int r[N], c[N], dgn[N+N], xdgn[N+N]; //x+y<=10 x-y+5<=9
ll ans;

void dfs(int i, int j, int bk, int wh)
{
  if(j > n) {i++, j = 1;}
  if(i > n)
  {
      ans++;
      return;     
  }

  if(bk)
  {
    r[i]++, c[j]++, dgn[i+j]++, xdgn[i-j+n]++;
    if(r[i] == n || c[j] == n || dgn[i+j] == n || xdgn[i-j+n] == n) return;
    dfs(i, j+1, bk-1, wh);
    r[i]--, c[j]--, dgn[i+j]--, xdgn[i-j+n]--;
  }

  if(wh)
  {
    r[i]--, c[j]--, dgn[i+j]--, xdgn[i-j+n]--;
    if(r[i] == -n || c[j] == -n || dgn[i+j] == -n || xdgn[i-j+n] == -n) return;
    dfs(i, j+1, bk, wh-1);
    r[i]++, c[j]++, dgn[i+j]++, xdgn[i-j+n]++;
  }
}
int main()
{
  int bk = (n * n + 1) / 2, wh = n * n / 2; //黑先手,bk=13,wh=12
  dfs(1, 1, bk, wh); //从(1,1)开始
  printf("%lld", ans);
}

这个不行

这是正确做法,但是多一道恢复的语句

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 6;

int n = 5;
int r[N], c[N], dgn[N+N], xdgn[N+N]; //x+y<=10 x-y+5<=9
ll ans;

void dfs(int i, int j, int bk, int wh)
{
  if(j > n) {i++, j = 1;}
  if(i > n)
  {
  	ans++;
  	return; 	
  }

  if(bk)
  {
    r[i]++, c[j]++, dgn[i+j]++, xdgn[i-j+n]++;
    if(r[i] == n || c[j] == n || dgn[i+j] == n || xdgn[i-j+n] == n) //不合理不递归,只恢复
    {
    	r[i]--, c[j]--, dgn[i+j]--, xdgn[i-j+n]--;
    }
    else //合理递归且恢复
    {
    	dfs(i, j+1, bk-1, wh);
    	r[i]--, c[j]--, dgn[i+j]--, xdgn[i-j+n]--;
    }
  }

  if(wh)
  {
    r[i]--, c[j]--, dgn[i+j]--, xdgn[i-j+n]--;
    if(r[i] == -n || c[j] == -n || dgn[i+j] == -n || xdgn[i-j+n] == -n)
    {
    	r[i]++, c[j]++, dgn[i+j]++, xdgn[i-j+n]++;
    }
    else
    {
    	dfs(i, j+1, bk, wh-1);
    	r[i]++, c[j]++, dgn[i+j]++, xdgn[i-j+n]++;
    }
  }
}
int main()
{
  int bk = (n * n + 1) / 2, wh = n * n / 2; //黑先手,bk=13,wh=12
  dfs(1, 1, bk, wh); //从(1,1)开始
  printf("%lld", ans);
}

另一种做法是直接试探合理性,不直接上手修改数据

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 6;

int n = 5;
int r[N], c[N], dgn[N+N], xdgn[N+N]; //x+y<=10 x-y+5<=9
ll ans;

void dfs(int i, int j, int bk, int wh)
{
  if(j > n) {i++, j = 1;}
  if(i > n)
  {
  	ans++;
  	return; 	
  }

  if(bk)
  {
    
    if(r[i] + 1 < n && c[j] + 1 < n && dgn[i+j] + 1 < n && xdgn[i-j+n] + 1 < n) //试探
    {
    	r[i]++, c[j]++, dgn[i+j]++, xdgn[i-j+n]++;
    	dfs(i, j+1, bk-1, wh);
    	r[i]--, c[j]--, dgn[i+j]--, xdgn[i-j+n]--;
    }
  }

  if(wh)
  {
    
    if(r[i] - 1 > -n && c[j] - 1 > -n && dgn[i+j] - 1 > -n && xdgn[i-j+n] - 1 > -n)
    {
    	r[i]--, c[j]--, dgn[i+j]--, xdgn[i-j+n]--;
    	dfs(i, j+1, bk, wh-1);
    	r[i]++, c[j]++, dgn[i+j]++, xdgn[i-j+n]++;
    }
  }
}
int main()
{
  int bk = (n * n + 1) / 2, wh = n * n / 2; //黑先手,bk=13,wh=12
  dfs(1, 1, bk, wh); //从(1,1)开始
  printf("%lld", ans);
}

代码

#include<bits/stdc++.h>
using namespace std;
 
int row[20];
int col[20];
int dia[20]; //对角线
int ndia[20]; //反对角线
int result;
void dfs(int n, int white, int black) //white+1, black-1
{
    if(n == 26)
    {
        result++;
        return;
    }
 
    // int x = (n+5-1) / 5; //一维的n映射到二维的(x,y)
    // int y = n%5;
    // if(!y) y = 5;
    
    int x = (n-1)/5+1;
    int y = (n-1)%5+1;
 
    if(white && row[x] < 4 && col[y] < 4 && dia[x-y+5] < 4 && ndia[x+y] < 4)
    {
        row[x]++;
        col[y]++;
        dia[x-y+5]++;
        ndia[x+y]++;
        dfs(n+1, white-1, black);
        row[x]--;
        col[y]--;
        dia[x-y+5]--;
        ndia[x+y]--;
    }
    if(black && row[x] > -4 && col[y] > -4 && dia[x-y+5] > -4 && ndia[x+y] > -4)
    {
        row[x]--;
        col[y]--;
        dia[x-y+5]--;
        ndia[x+y]--;
        dfs(n+1, white, black-1);
        row[x]++;
        col[y]++;
        dia[x-y+5]++;
        ndia[x+y]++;
    }
 
    return;
}
int main()
{
    dfs(1, 13, 12);
 
    cout << result << endl;
 
    return 0;
}

重写

#include <bits/stdc++.h>
using namespace std;
const int N = 20;
int r[N], c[N], dg[N], xdg[N];
int n = 5, ans;
void dfs(int pos, int w, int b)
{
    if (pos == n * n + 1)
    {
        ans++;
        return;
    }

    int x = (pos - 1) / n + 1;
    int y = (pos - 1) % n + 1;

    if (w && r[x] < n - 1 && c[y] < n - 1 && dg[x + y] < n - 1 && xdg[x - y + n] < n - 1)
    {
        r[x]++;
        c[y]++;
        dg[x + y]++;
        xdg[x - y + n]++;
        dfs(pos + 1, w - 1, b);
        r[x]--;
        c[y]--;
        dg[x + y]--;
        xdg[x - y + n]--;
    }

    if (b && r[x] > 1 - n && c[y] > 1 - n && dg[x + y] > 1 - n && xdg[x - y + n] > 1 - n)
    {
        r[x]--;
        c[y]--;
        dg[x + y]--;
        xdg[x - y + n]--;
        dfs(pos + 1, w, b - 1);
        r[x]++;
        c[y]++;
        dg[x + y]++;
        xdg[x - y + n]++;
    }
}
int main()
{
    dfs(1, (n * n + 1) / 2, n * n / 2);
    cout << ans;
}

<think>好的,我现在要处理用户关于蓝桥杯五子棋对弈的问题。首先,用户需要了解如何实现这个五子棋对弈,或者找到相关资料。我需要先回顾蓝桥杯往届题目,尤其是2024年省赛A五子棋题目。 用户提到的引用[1]是关于比赛规则的,比如5x5棋盘,平局的要求,可能还有胜负判定方法。首先,我得考虑五子棋的基本规则,比如连成五子算赢,但在这个题目中可能有特殊规则,比如棋盘较小,或者和棋的条件。 接下来,实现五子棋对弈需要几个关键部分:棋盘表示、落子处理、胜负判定、用户交互。对于棋盘,可以用二维数来表示每个位置的状态(黑、白、空)。落子处理需要交替下棋,检查位置是否合法。胜负判定是关键,每次落子后检查横向、纵向、对角线是否有连续五子。但因为是5x5棋盘,可能有些情况下无法形成五连珠,从而出现和棋。 这里需要注意,题目中提到双方希望和棋作为友谊见证,但通常五子棋在5x5的棋盘上可能更容易出现胜负,所以可能需要特殊规则来确保和棋的可能性,比如禁止某些走法或者平局条件。不过根据用户提供的引用,可能规则是常规的,胜负判定后若无法分出胜负则平局。 然后,胜负判定的算法需要高效。每次落子后,检查四个方向(水平、垂直、两个对角线)是否有连续五个相同颜色的棋子。由于棋盘是5x5,所以任何位置最多只能形成五连,所以需要遍历所有可能的方向。 另外,用户可能需要代码示例,比如用Python实现。需要考虑如何表示棋盘,处理输入,判断胜负等。例如,定义一个5x5的二维数,轮流输入坐标,每次下棋后调用判断函数。 可能的问题点:如何判断四个方向是否有五连?例如,对于每个新下的棋子位置(x,y),检查四个方向的最大连续数。或者,由于棋盘小,可以遍历整个棋盘检查所有可能的五连情况,但效率不高。更高效的方法是仅检查当前落子周围可能形成的连线。 然后,用户的问题可能还包括如何实现和棋的情况,比如棋盘填满且没有五连时判定平局。这时需要检查棋盘是否已满。 现在,我需要整理这些思路,形成步骤清晰的回答,包括棋盘表示、落子处理、胜负判定、和棋条件,并给出代码框架。同时,生成相关问题,比如胜负判定算法优化、如何实现禁手规则、棋盘交互设计等。 还需要确保回答中的数学公式和代码符合用户指定的格式,比如行内公式用$...$,独立公式用$$...$$,代码块用```包裹,正确的中文语法,以及相关问题的生成。 最后,检查是否有引用文献,用户提供的引用[1]是关于比赛规则的,需要在回答中适当的位置引用,比如在介绍规则时提到。</think>关于蓝桥杯五子棋对弈的实现方法,以下是关键步骤和技术要点分析: ### 一、棋盘表示与规则 1. **数据结构** 使用$5 \times 5$的二维数表示棋盘,元素值可为$0$(空)、$1$(黑子)、$2$(白子)。例如: ```python board = [[0 for _ in range(5)] for _ in range(5)] ``` 2. **特殊规则** 根据蓝桥杯题目描述[^1],双方需在5x5棋盘上实现和棋。胜负判定需满足: - 横向、纵向或斜向形成连续五子的一方获胜 - 棋盘填满且无五连时为和棋 ### 二、核心算法实现 1. **胜负判定算法** 每次落子后,检查四个方向的最大连续数: ```python def check_win(x, y, color): directions = [(1,0), (0,1), (1,1), (1,-1)] # 右/下/右下/左下 for dx, dy in directions: count = 1 for step in [-1, 1]: # 双向检查 tx, ty = x + step*dx, y + step*dy while 0 <= tx <5 and 0 <= ty <5 and board[tx][ty] == color: count += 1 tx += step*dx ty += step*dy if count >=5: return True return False ``` 2. **和棋判定** 遍历棋盘判断是否填满: ```python def is_draw(): return all(cell !=0 for row in board for cell in row) ``` ### 三、完整实现框架 ```python class Gomoku: def __init__(self): self.board = [[0]*5 for _ in range(5)] self.current_player = 1 # 黑方先行 def make_move(self, x, y): if self.board[x][y] !=0: return False # 非法落子 self.board[x][y] = self.current_player if self.check_win(x, y): return "WIN" if self.is_draw(): return "DRAW" self.current_player = 3 - self.current_player # 切换玩家 return "CONTINUE" ``` ### 四、性能优化方向 1. **剪枝策略**:胜负判定时发现$count \geq5$立即返回 2. **增量计算**:仅检查最新落子相关的连线方向 3. **位运算优化**:使用位棋盘加速状态判断(适用于C/C++实现)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值