博客园同步

原题链接

2020.4.1

在这里立个 flag\text{flag}flag:一周内 AC\texttt{AC}AC 不了这道题目,我就 倒 ! 立 ! 洗 ! 头 !

本人还没 AC\texttt{AC}AC,不过呢,谔谔,先整理下思路吧。

简要题意:

给定一个棋局,双方轮流将自己的棋子走到空位(任意方先走),问使得“四子连棋”(即四个子在一条直线上,斜线也可以)的最小步数。

显然,4×44 \times 44×4 的棋盘只要不出问题就能 AC\text{AC}AC.(但我。。。)

本题需要优化搜索!(其实不然)

假设你用纯属的 bfs\texttt{bfs}bfs,旁边开着哈希,你完全 有可能将所有状态都遍历一遍

而所有状态的种数是(根据组合计算):

16!7!×7!×2!=411840\frac{16!}{7! \times 7! \times 2!} = 4118407!×7!×2!16!​=411840

然后,你映射到哈希还需要 42=164^2 = 1642=16 的时间,然后还要每次判断答案再用 42=164^2 = 1642=16 的时间,其它的不说,时间复杂度达到了:

O(4×105×162)>O(108)O(4 \times 10^5 \times 16^2) > O(10^8)O(4×105×162)>O(108)

再快的评测机,你广搜的常数也不会小的,然后妥妥的 T\text{T}T 掉了。。

你说:好,我用双向 bfs\text{bfs}bfs.

没错确实可以,但这边不讲 究竟是懒,还是博主不会,大家细细品!

那么 A*\text{A*}A* 是什么呢?(这么神秘)

其实,A*\text{A*}A* 的搜索可以保证每次的状态越来越优(至少也不会变劣)。

假设当前状态为 xxx,转移状态为 yyy,对状态的咕值函数为 fff(没错就是这个咕,咕咕咕的咕),则必须满足:

fx≤fyf_x \leq f_yfx​≤fy​

此时才可以进行转移,否则 A*\text{A*}A* 就直接把它结束。

为什么呢?

你想,如果你转移一步状态反而变劣了,那肯定还不如不转,对不对?

那么,这个 咕值函数 在本题中是什么呢?

就是 最大的连续棋子

也就是说,如果你走了一步,原来连续的几个棋子被你破坏导致答案变劣,那这步就可以去世了。

那你说,如果我在破坏一个的同时又创造了新的一个呢?那不会影响,因为你新的一个如果还是较劣也会被剔除,否则这步就是可以的。

至少,A*\text{A*}A* 满足状态越来越优(至少不会变劣),那么就非常的好。(好!好!好!)

这样可以认为是一个 可行性剪枝 但是创造者为了提高咕值就把它列成算法怎么地

但是,在本题中,这个优化没有什么用处。

因为一个状态至少有 222 个连续的棋子(反证可知),所以如果原棋盘的咕值是 222,那么还是会遍历的。

不过如果是 333 的话那瞬间时间复杂度就降到地下了。。(不过随机数据还没这个概率)

时间复杂度:O(wys)O(\texttt{wys})O(wys).(玄学,因为完全不会遍历所有状态)

实际得分:80pts80pts80pts.(本人不知道哪里写炸了,如果有误可以在讨论区提出!)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}struct node{char p[5][5];int step; char tt; //tt 表示上一局的走棋方
};char a[5][5];
map<string,int> h;
queue<node> q;const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};inline node mp(char a[5][5],int x,char tt) {node t; t.tt=tt; for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) t.p[i][j]=a[i][j];t.step=x; return t;
} //将数据整装成结构体inline string turn(char a[5][5]) {string st="";for(int i=1;i<=4;i++)for(int j=1;j<=4;j++) st+=a[i][j];return st;
} //压缩成字符串(其实3进制状压也行)inline pair<int,int> get(char a[5][5],int x,int y) {for(int i=1;i<=4;i++) for(int j=1;j<=4;j++)if(a[i][j]=='O' && (i!=x || j!=y))return make_pair(i,j);
} //寻找不在 (x,y) 位置的 O ,第一次找到后可以顺便找第二次inline int f(char a[5][5]) {int maxi=1; for(int i=1;i<=4;i++) {if(a[i][1]==a[i][2] && a[i][2]==a[i][3] && a[i][3]==a[i][4]) return 4;if(a[i][2]==a[i][3] && (a[i][1]==a[i][2] || a[i][3]==a[i][4])) maxi=max(maxi,3);else if(a[i][1]==a[i][2] || a[i][2]==a[i][3] || a[i][3]==a[i][4]) maxi=max(maxi,2); //横着} for(int i=1;i<=4;i++) {if(a[1][i]==a[2][i] && a[2][i]==a[3][i] && a[3][i]==a[4][i]) return 4;if(a[2][i]==a[3][i] && (a[1][i]==a[2][i] || a[3][i]==a[4][i])) maxi=max(maxi,3);else if(a[1][i]==a[2][i] || a[2][i]==a[3][i] || a[3][i]==a[4][i]) maxi=max(maxi,2); //竖着} bool l[5][5]; memset(l,0,sizeof(l));for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) {if(l[i][j]) continue;int x=i,y=j;while(x<=3 && y<=3 && a[x+1][y+1]==a[x][y]) l[++x][++y]=1;maxi=max(maxi,x-i+1); //往右下角走} memset(l,0,sizeof(l));for(int i=1;i<=3;i++) for(int j=2;j<=4;j++) {if(l[i][j]) continue;int x=i,y=j;while(x<=3 && y>=2 && a[x+1][y-1]==a[x][y]) l[++x][--y]=1;maxi=max(maxi,x-i+1); //往左下角走} return maxi;
} //咕值函数/*inline void print(char a[5][5],int step) {putchar('\n');for(int i=1;i<=4;i++) {for(int j=1;j<=4;j++) putchar(a[i][j]);putchar('\n');} printf("%d\n",step);
}*/ //调试的代码inline void bfs() {q.push(mp(a,0,'O')); h[turn(a)]=1;while(!q.empty()) {node t=q.front(); char now[5][5]; int step;for(int i=1;i<=4;i++) for(int j=1;j<=4;j++)now[i][j]=t.p[i][j]; step=t.step; //取出队首q.pop();  //print(now,step); //出队pair<int,int> pr=get(now,0,0);pair<int,int> pr2=get(now,pr.first,pr.second); //得到两个位置
//      printf("%d %d %d %d\n",pr.first,pr.second,pr2.first,pr2.second);for(int i=0;i<4;i++) {int x=dx[i]+pr.first,y=dy[i]+pr.second;if(x<1 || y<1 || x>4 || y>4 /*|| now[x][y]==t.tt*/ || now[x][y]=='O' || now[x][y]==t.tt) continue;//不是上一局的那一方,并且不是两个空位转化swap(now[x][y],now[pr.first][pr.second]);int u=f(now),v=f(t.p);if(u<v || h[turn(now)]) { //变劣 或 已出现swap(now[x][y],now[pr.first][pr.second]); continue; }h[turn(now)]=1;   q.push(mp(now,step+1,a[x][y])); //入队,标记if(u==4) { //达到目标状态
//              print(now,step+1);printf("%d\n",step+1);exit(0);} swap(now[x][y],now[pr.first][pr.second]); //记得再换回去} pr=pr2; //对第二个位置同样做一遍for(int i=0;i<4;i++) {int x=dx[i]+pr.first,y=dy[i]+pr.second;if(x<1 || y<1 || x>4 || y>4 /*|| now[x][y]==t.tt*/ || now[x][y]=='O' || now[x][y]==t.tt) continue;swap(now[x][y],now[pr.first][pr.second]);int u=f(now),v=f(t.p);if(u<v || h[turn(now)]) {swap(now[x][y],now[pr.first][pr.second]);continue; }h[turn(now)]=1; q.push(mp(now,step+1,a[x][y]));if(u==4) {//              print(now,step+1);printf("%d\n",step+1);exit(0);} swap(now[x][y],now[pr.first][pr.second]);}}
}int main(){for(int i=1;i<=4;i++)for(int j=1;j<=4;j++) cin>>a[i][j];if(f(a)==4) puts("0"); //特判一开始就是目标状态else bfs();return 0;
}

P2346 四子连棋 题解相关推荐

  1. codevs 1004 四子连棋 BFS、hash判重

    004 四子连棋 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色 ...

  2. [Codevs] 1004 四子连棋

    1004 四子连棋  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子, ...

  3. 【codevs1004】四子连棋

    problem solution codes //思路:把空白当棋,交替黑白走. //实现:BFS, 打表判断是否成立 #include<iostream> #include<alg ...

  4. java四子棋实验报告_Python 实现劳拉游戏的实例代码(四连环、重力四子棋)

    游戏规则:双方轮流选择棋盘的列号放进自己的棋子, 若棋盘上有四颗相同型号的棋子在一行.一列或一条斜线上连接起来, 则使用该型号棋子的玩家就赢了! 程序实现游戏,并将每局的数据保存到本地的文件中 首先我 ...

  5. python人机对战的实验步骤_人机对战初体验:Python实现四子棋游戏

    继去年3月人机大战引发全球瞩目以来,围棋AI(人工智能)再度引发跨领域的关注:一个叫Master的围棋AI,几天时间,面对中日韩顶尖职业围棋选手,已取得60胜0败的恐怖战绩,展现出的围棋技艺已经到了人 ...

  6. 四子棋 freepython

    四子棋,是黑白棋的一种.是一种益智的棋类游戏.黑白两方(也有其它颜色的棋子)在8*8的格子内依次落子.黑方为先手,白方为后手.落子规则为,每一列必须从最底下的一格开始.依此可向上一格落子.一方落子后另 ...

  7. 深圳大学计系汇编语言实验--四子棋游戏

    题面 四子棋是个双人游戏,两人轮流下棋,棋盘由行和列组成的网格,每个选手每次下一个子直到两人中有一人的棋子连成一条水平线.垂直线或者是对角线. 本实验需要在LC-3中实现简易版四子棋的游戏,两位选手通 ...

  8. 四子棋游戏--bingo game

    最近自己编了一个四子棋的游戏.说明如下: 游戏简介: 一种常见的四子棋游戏,可以是人机对战,两人对战,或者网上对战. 游戏的双方轮流落子,每人持有21颗子.棋子共有6*7个位置,玩家的棋子总是落到 当 ...

  9. 基于LC3模拟器的简单游戏设计:简易四子棋

    一.实验目的 分析和理解指定的需解决问题. 利用LC-3的汇编代码设计实现相关程序. 通过LC-3仿真器调试和运行相关程序并得到正确的结果. 二.实验内容 四子棋是一款普遍流行的简易型桌面游戏,据说, ...

  10. vb四环棋的实现,平面四子棋,四连环游戏

    vb四环棋的实现,平面四子棋 首先我们百度一下,什么是平面四子棋 相信很多小伙伴见到这幅图片都不陌生. 那么在代码中怎么实现呢?我们用vb代码为例子. 先看效果图 我们设计o和x是需要下的棋子,如果没 ...

最新文章

  1. leetcode算法题--二叉树中的最长交错路径★
  2. javaweb功能模块如何合理设计_产品设计:如何设计出合理的凑单模式?
  3. MariaDB(MySQL)数据库的介绍及使用示例
  4. flex页面中嵌入html页面
  5. Java NIO vs IO
  6. Java—简单的注册页面
  7. 一文带你轻松搞懂事务隔离级别(图文详解)
  8. github 生产环境_如何在GitHub上提高生产力
  9. python自动化运维平台下载_5、python自动化运维——集中化管理平台Ansible
  10. 29.FFmpeg+OpenGLES+OpenSLES播放器实现(三.FFmpeg配置和编译脚本)
  11. linux格式化硬盘怎么那么慢,linux系统中怎么格式化硬盘
  12. Linux下简单的木马查杀
  13. CVPR 2022 | End-to-End Referring Video Object Segmentation with MultimodalTransformers
  14. 智能车入门——跑车前的零碎知识<新手从零做车>
  15. 什么是域名?域名详细介绍
  16. 免费个人数据备份软件介绍:FreeFileSync、Syncthing
  17. python中cat,stac,transpose,permute,squeeze区别用法
  18. java 类的方法设计_设计问题:Java类与单方法OK?
  19. 联发科AIoT平台处理器芯片i500介绍
  20. 安卓3d游戏插件_微信创意小游戏:消灭病毒流水超3.5亿,5款累计流水过千万

热门文章

  1. 计算机专业数据结构试题答案,2021考研计算机408数据结构试题及答案解析
  2. 管家婆 凭证查找 Date exceeds maximum of 19-12-31 报错解决办法
  3. java发送带附件的电子邮件
  4. 福昕阅读器3.1.3.1030破解方法
  5. C# 实现对三维点数据的 显示
  6. 基于FPGA/数字IC的数字信号处理课程
  7. Python 求峰值
  8. 软件项目开发流程逻辑图
  9. 我行我素购物管理系统(面向对象)
  10. c语言实验11实验报告,c语言 实验报告11 12.doc