连连看(DFS)

连连看(图)

[问题描述]

建立一个10*20的矩形方格图,其中有10种不同的图案,每种图案个数为偶数,填满矩形方格图。

[基本要求]

(1)随机产生原始数据;

(2)输入两个位置,如果两者图案相同,并且可以用小于等于3条直线相连,即可消除该两个图案。

解题思路:

1、我们首先要做的是构建方格图。这里我们用A到J(对应数字1到10)这10个大写字母来表示10种不同的图案。因为要求每种图案的个数都为偶数,所以我们要先确定每种图案的个数(确保都是偶数,且总和为200)。然后再依次遍历每一格,对每一格,我们从尚有剩余的图案中通过随机数来随机选定一个图案填上去。

2、之后就是 play game 的阶段了。我们首先要明确游戏规则:(1)能消去的图案必须相同。(2)两个图案可以用小于等于3条直线相连。这里解释一下,将两个图案连接在一起的直线可以视为一个路径。路径必须是可以到达(即通过空的位置连接),在最开始图案全满的时候,只能消除相邻的两个图案或位于同一边缘的两个图案。另外,小于等于3条直线,意味着路径最多包含三条直线(即最多只能拐弯两次)。这里,我们可以使用DFS,从一点出发去搜索另一个点,并时刻统计拐弯的次数,若拐弯次数大于2,则终止这一条路径的搜索。若最后,有拐弯次数小于等于2的到达路径,则说明可以成功消去。

注意:为了提高程序的鲁棒性,要进行输入检查。输入必须满足两个位置不能越界且都有图案存在。至于能不能成功消去,要看两个图案是否相等且能否找到两个图案之间的通路。对已经消去的图案,我们用 * (对应数字0)来填补,主要是便于后面消去时确定位置。另外,由于位于同一边缘的两个图案也可以消去,所以我们在开数组空间时,要在原来方格图的大小进行适当扩充

程序代码:

# include <iostream>
# include <random>
# include <time.h>
# define LONG 20   
# define WIDTH 10 
# define SIZE 200
# define NUM 10
using namespace std;

//随机生成图案
void Create();
//打印图案
void Print();
//输入判断
bool Judge(int a, int b, int c, int d);
//搜索路径
void DFS(int x, int y, int x2, int y2, int d, int k);
bool Find(int x1, int y1, int x2, int y2);
//玩游戏
void PlayGame();

int g[WIDTH + 2][LONG + 2] = { {0} };  //图像
int way[4][2] = { {0,1},{0,-1},{1,0},{-1,0} };  //前进方式
int visit[WIDTH + 2][LONG + 2] = { {0} };       //标记走过的地方
bool temp;          //是否存在路径

int main()
{
	Create();
	cout << "原图:" << endl;
	Print();
	PlayGame();
	return 0;
}

//随机生成图案
void Create()
{
	int n, sum = 0;
	int num[NUM + 1];   //每种图案的个数
	srand((unsigned int)time(0)); //修改种子
	for (int i = 1; i < NUM; i++) {
		//确定每种图案的个数
		n = rand() % SIZE + 1;
		if (n % 2 == 0 && sum + n < SIZE) {
			sum += n;
			num[i] = n;
		}
	}
	num[NUM] = SIZE - sum;  //直接求最后一种图案的个数
	for (int i = 1; i <= WIDTH; i++) {
		for (int j = 1; j <= LONG; j++) {
			n = rand() % NUM + 1;  //随机选取一种图案
			while (!num[n]) {
				//如果这种图案已放置完,跳到下一个
				n = (n + 1) % NUM;
				if (n == 0) {
					n = 10;
				}
			}
			g[i][j] = n;
			num[n]--;
		}
	}
}

//打印图案
void Print()
{
	char c;
	for (int i = 1; i <= WIDTH; i++) {
		for (int j = 1; j <= LONG; j++) {
			if (g[i][j]) {
				c = 64 + g[i][j];
				cout << c;
			}
			else {
				//已消去的地方用*补齐
				cout << "*";
			}
		}
		cout << endl;
	}
}

//输入判断
bool Judge(int a, int b, int c, int d)
{
	//越界检查
	if (a<1 || a>WIDTH || c<1 || c>WIDTH) {
		return false;
	}
	if (b<1 || b>LONG || d<1 || d>LONG) {
		return false;
	}
	//检查对应位置是否存在图案
	if (!g[a][b] || !g[c][d]) {
		return false;
	}
	return true;
}

//搜索路径
//(x,y)当前位置
//d是上一次的前进方向(0,1,2,3)
//k是已拐弯的次数
void DFS(int x, int y, int x2, int y2, int d, int k)
{
	if (x == x2 && y == y2 && k <= 2) {
		//到达终点,且拐弯次数小于等于2,可以成功消去
		temp = true;
		return;
	}
	if (k > 2) {
		return;
	}
	for (int i = 0; i < 4; i++) {
		int p1 = x + way[i][0];
		int p2 = y + way[i][1];
		if (visit[p1][p2] || p1<0 || p1>WIDTH + 1 || p2<0 || p2>LONG + 1) {
			//走过或越界的路淘汰
			continue;
		}
		if (g[p1][p2] && (p1 != x2 || p2 != y2)) {
			//不是终点,但有图案的路淘汰
			continue;
		}
		if (i == d || d == -1) {
			//没有拐弯
			visit[p1][p2] = 1;
			DFS(p1, p2, x2, y2, i, k);
		}
		else {
			//拐弯
			visit[p1][p2] = 1;
			DFS(p1, p2, x2, y2, i, k + 1);
		}
		//数据恢复
		visit[p1][p2] = 0;
	}
}

bool Find(int x1, int y1, int x2, int y2)
{
	temp = false;
	for (int i = 0; i <= WIDTH + 1; i++) {
		for (int j = 0; j <= LONG + 1; j++) {
			visit[i][j] = 0;
		}
	}
	DFS(x1, y1, x2, y2, -1, 0);
	return temp;
}

//玩游戏
void PlayGame()
{
	bool t;
	int x1, y1, x2, y2; //位置坐标(x1,y1),(x2,y2)
	cout << "\n游戏开始!(提示:输入0游戏结束)\n";
	cout << "请输入两个位置:";
	cin >> x1;
	while (x1) {
		cin >> y1 >> x2 >> y2;
		if (!Judge(x1, y1, x2, y2)) {
			cout << "坐标错误,请重新输入:";
		}
		else {
			if (g[x1][y1] != g[x2][y2]) {
				cout << "消去失败" << endl;
			}
			else {
				t = Find(x1, y1, x2, y2);
				if (t) {
					//符合消去要求
					g[x1][y1] = 0;
					g[x2][y2] = 0;
					cout << endl;
					Print();
				}
				else {
					cout << "消去失败" << endl;
				}
			}
			cout << "请输入两个位置:";
		}
		cin >> x1;
	}
}

运行结果:

原图:
CEHDEFFIAAGJBDJHBFGH
DBHACEEHGHGDFEJIJHFD
CBEAGDFHDCEJEFGHIJJG
IDHFDFHGIDFJJBECEGGA
HHIBBJADHCCEDDAABIIH
CCDAFHFEDGHAEDGIDEFI
BHECGADHDDGABGCADGDC
DDBICDABDFBAACAABBHE
AGEDHEHIGCBBIGACCEEG
FGDBACCDGAADBAACIABA

游戏开始!(提示:输入0游戏结束)
请输入两个位置:1 2 1 5

C*HD*FFIAAGJBDJHBFGH
DBHACEEHGHGDFEJIJHFD
CBEAGDFHDCEJEFGHIJJG
IDHFDFHGIDFJJBECEGGA
HHIBBJADHCCEDDAABIIH
CCDAFHFEDGHAEDGIDEFI
BHECGADHDDGABGCADGDC
DDBICDABDFBAACAABBHE
AGEDHEHIGCBBIGACCEEG
FGDBACCDGAADBAACIABA
请输入两个位置:1 1 2 5

**HD*FFIAAGJBDJHBFGH
DBHA*EEHGHGDFEJIJHFD
CBEAGDFHDCEJEFGHIJJG
IDHFDFHGIDFJJBECEGGA
HHIBBJADHCCEDDAABIIH
CCDAFHFEDGHAEDGIDEFI
BHECGADHDDGABGCADGDC
DDBICDABDFBAACAABBHE
AGEDHEHIGCBBIGACCEEG
FGDBACCDGAADBAACIABA
请输入两个位置:0

以上便是我对这道题的看法,很高兴与大家分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值