本文专栏:研究生课程 点击查看系列文章
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
输出结果
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
运行结果如上图所示。棋盘中的C为战车位置,B为轰炸区。