【课程·研】算法 | 战车问题(概率问题)

本文专栏:研究生课程  点击查看系列文章

1. 问题描述

在棋盘上放置彼此不受攻击的战车。其中,战车可以攻击与之处在同一行或同一列上的战车。在棋盘上的若干个格中设置了堡垒,战车无法穿越堡垒攻击别的战车。对于给定的设置了堡垒的n×n格棋盘,设计一个概率算法,在棋盘上放置尽可能多的彼此不受攻击的战车。

2. 输入输出示例

输入

​ 第1行有1个正整数n

接下来的n行中,每行有1个由字符“.”和“X”组成的长度为n的字符串

其中“X”代表堡垒,“.”处表示可以放战车的位置

输出

​ 在棋盘上可以放置的彼此不受攻击的战车数目

样例输入

4
....
..X.
.X..
....

样例输出

6

3. 算法设计思想与算法描述

首先分析本题。在一个n*n的棋盘上,随机选取了一部分交点,放置战车。但是,战车可以攻击同一行或者同一列的战车。不过,棋盘中部分节点设置为堡垒,可以阻挡战车之间的攻击。题目要求求出放置战车的最大数量。

常规的算法思路,是按行来遍历搜索解决该问题,即按行遍历所有可以放置战车的节点,如果该节点可以放置战车(同一行或同一列无战车,或有堡垒间隔)。如此循环,直至最后一个节点。

而本题,则采用概率算法进行算法设计。即对于某一行,随机选取一个可能放战车的列,对这个点进行判断,如果可以放战车,则将其标记为已放战车的标记C,然后把该点上、下、左、右的相邻的,可以被该战车攻击到的点(即没有被堡垒分隔的点),标记为轰炸区,用B表示。然后对于第一行,执行执行结束后,再对第二行执行这种操作。这一遍执行完毕后,再从头重新执行多次,然后从所有的结果中,选取战车数量最多的棋盘,即为最终所求。

所以,本概率算法的算法描述如下所示。

算法描述:

输入数据
for 循环g_count次,选最优解 do
  初始化临时棋盘
  复制初始棋盘布局至临时棋盘
  设置当前循环最大战车数num
  for 遍历所有的行 do
    设置标记flag=false
    do
      for 循环所有的列 do
        if 当前坐标可放战车(不是堡垒,未在其他战车轰炸区)
          flag=true
          生成随机数R
          if 坐标[i][R]处的点可放战车
            放上战车C
            该战车上下左右设置为轰炸区B(堡垒之内)
          end if
        else
          flag = false
        end else
      end for
    while flag=true
  end for
  if 本轮操作放置战车数目大于以往
    最终棋盘布局替换为本次
    最终战车数目sum替换为本次
  end if
end for
输出结果

image-20210216154943422

4. 算法实现

根据前文的算法描述,采用C++高级语言实现了该算法。

// 战车放置问题.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
using namespace std;

const int g_count = 10;					//设置默认循环次数
const int g_size = 1000;					//设置默认棋盘大小

//参数:. - 可放战车  X-堡垒  C-已放战车  B-可攻击点
char chess[g_size][g_size] = { '.' };		//棋盘初始化
char chess_final[g_size][g_size] = { '.' };	//最终棋盘布局
char chess_temp[g_size][g_size] = { '.' };	//临时棋盘
int n = 4;			//棋盘大小
int max_car = 0;	//能放的最多战车数

/*
	判断该点能否放置战车
	chess_temp:棋盘布局
	n:棋盘大小
	i:棋盘的行号
	j:棋盘的列号
*/
bool check(int n,int i, int j) {
	//如果该点不是‘.’,不可放置,返回假
	if (chess_temp[i][j] != '.') {
		return false;
	}
	//反之,可以放,返回真,并将与该点有关的上下左右设置为可轰炸点
	else {
		chess_temp[i][j] = 'C';

		//修改该点左侧、非堡垒分割的点为轰炸点【B】
		for (int k = j - 1; k >= 0; k--) {
			//如果是堡垒,结束
			if (chess_temp[i][k] == 'X')
				break;
			//反之,修改为轰炸点
			else
				chess_temp[i][k] = 'B';			
		}

		//修改该点右侧、非堡垒分割的点为轰炸点【B】
		for (int k = j + 1; k < n; k++) {
			//如果是堡垒,结束
			if (chess_temp[i][k] == 'X')
				break;
			//反之,修改为轰炸点
			else
				chess_temp[i][k] = 'B';
		}
		//修改该点上侧、非堡垒分割的点为轰炸点【B】
		for (int k = i - 1; k >= 0; k--) {
			//如果是堡垒,结束
			if (chess_temp[k][j] == 'X')
				break;
			//反之,修改为轰炸点
			else
				chess_temp[k][j] = 'B';
		}
		//修改该点左侧、非堡垒分割的点为轰炸点【B】
		for (int k = i + 1; k < n; k++) {
			//如果是堡垒,结束
			if (chess_temp[k][j] == 'X')
				break;
			//反之,修改为轰炸点
			else
				chess_temp[k][j] = 'B';
		}
		return true;
	}	
}


int main()
{
	//输入基本数据
	cout << "请输入棋盘大小:";
	cin >> n;
	cout << "请按棋盘大小输入棋盘初始布局:" << endl;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++)
		{
			cin >> chess[i][j];
		}
	}

	//测试输出
	/*
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++)
		{
			cout << chess[i][j]<<" ";
		}
		cout << endl;
	}
	*/

	//循环g_count次,得出最优解
	for (int time = 0; time < g_count; time++) {
		//int R = (rand() % (n));//生成[0,n)的随机整数
		//测试
		

		//初始化临时棋盘
		chess_temp[g_size][g_size] = { '.' };
		//复制棋盘
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				chess_temp[i][j] = chess[i][j];
			}
		}

		//设置当前遍历的最大战车数
		int num = 0;
		//遍历所有的行
		for (int i = 0; i < n; i++)
		{
			
			bool flag = false;
			//遍历该行所有的列,找能放的点
			do
			{
				//遍历所有的列
				for (int j = 0; j < n; j++)
				{
					//cout << "当前分析第:" << i << "   " << j << endl;
					//未被占用,能放战车,未在轰炸区
					if (chess_temp[i][j] == '.') {
						flag = true;

						int R = (rand() % (n));//生成[0,n)的随机整数
						//cout << "while循环:随机数:" << j << endl;
						//判断该点能否真的放置战车【是否会被轰炸】
						if (check(n, i, R))
							//能放,战车数+1
							num++;
						break;
					}
					else {
						flag = false;
					}					
				}	
				
			} while (flag);
			
			/**
			for (int a = 0; a < n; a++) {
				for (int b = 0; b < n; b++)
				{
					cout << chess_temp[a][b] << " ";
				}
				cout << endl;
			}*/
		}

		//修改最大放置的战车数目
		if (max_car < num) {
			max_car = num;
			//复制棋盘
			for (int i = 0; i < n; i++)
			{
				for (int j = 0; j < n;j++) {
					chess_final[i][j] = chess_temp[i][j];
				}
			}			
		}
	}

	//输出最终结果
	cout << "最终结果:" << endl;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++)
		{
			cout << chess_final[i][j] << " ";
		}
		cout << endl;
	}
	cout << "最多可放战车数:"<<max_car << endl;

	return 0;
}

5. 测试结果

控制台输入数据,然后执行算法。两组测试结果(输入与输出)如下文所示。

测试用例1

输入数据:4 { . . . . . . X . . X . . . . . .}

输出数据:6

测试用例2

输入数据:6 {… .X…X. … …X… …X X……}

输出数据:9

image-20210216155122131

运行结果如上图所示。棋盘中的C为战车位置,B为轰炸区。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾年之璐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值