问题描述:
世界名画陈列馆由m*n个排列成矩形阵列的陈列室组成。为了防止名画被盗,需要在陈列室中设置警卫机器人哨位。除了监视所在的陈列室,每个警卫机器人还可以监视与它所在的陈列室相邻的上、下、左、右4个陈列室。试设计一个安排警卫机器人哨位的算法,使名画陈列馆中每个陈列室都在警卫机器人的监视下,并且要求每个陈列室仅受一个警卫机器人监视,且所用的警卫机器人数最少。
算法设计:
设计一个算法,计算警卫机器人的最佳哨位安排方案,使名画陈列馆中每个陈列室仅受一个警卫机器人监视。且所使用的警卫机器人数最少。
数据输入:
由文件Input.txt提供输入数据。文件的第1行有2个正整数m和n(1<=m,n<=20)。
结果输出:
将计算的警卫机器人数及其最佳哨位安排输出到文件output.txt。文件的第一行是警卫机器人数:接下来的m行中每行n个数,0表示无哨位,1表示哨位。如果不存在满足要求的哨位安排方案,则输出“No Solution!”。
输入文件示例
Input.txt output.txt
4 4 4
0 0 1 0
1 0 0 0
0 0 0 1
0 1 0 0
设计思路
对于这m*n个陈列室,我们从第一个陈列室开始遍历,从左到右从上到下,先判断这个位置可以放机器人吗,如果可以,我们先摆一个在这上面,然后继续遍历下一个陈列室,直到遍历完所有的陈列室。判断是否满足不重复全部监视的条件,满足就保存好当前的机器人摆放情况。
然后我们进行回溯,可以摆放机器人,如果我们不摆放呢?然后继续遍历后面的陈列室,对每一个陈列室都做这样的处理,即可枚举出所有的情况。对所有情况,我们总是保存最小的机器人数和此时机器人的摆放情况。
如果直到所有情况都列举完,仍然没有摆放的方案,那么输出no solution,否则输出摆放情况。
对于回溯的情况,我们还可以进行剪枝(否则越到后面越复杂,会花费大量的时间),那就是判断一下如果不摆是否就一定不成功了,如果是,那么就必须要摆。最后我测试了一下m=[1,25]和n=[1,25],发现从n>=3,m>=5开始,都是没有解的,所以也可以根据这个情况进行剪枝,会大大缩短mn较大时的运行时间。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<limits.h>
#include<time.h>
#define MAX 30
int m , n , best;//m*n,最优解的机器人数
int robots[MAX][MAX];//表示警卫机器人情况
int bestRobots[MAX][MAX];//存放当前最优的机器人安排情况
int monitor[MAX][MAX];//表示受监视情况
//从文件中读取m,n
void readFile ()
{
FILE*f = fopen ( "D:\\学习\\大三上\\算法设计与分析\\实验5报告+附件\\input(2).TXT" , "r" );
if (f == NULL)
{
printf ( "读取文件错误" );
return;
}
fscanf_s ( f , "%d%d" , &m , &n );
fclose ( f );
}
//初始化数据
void initData ()
{
best = INT_MAX;
memset ( monitor , 0 , sizeof ( monitor ) );
memset ( robots , 0 , sizeof ( robots ) );
}
//在控制台输出机器人安排情况并打印到文件中
void printArrange ()
{
FILE* file = fopen ( "D:\\学习\\大三上\\算法设计与分析\\实验5报告+附件\\output(2).TXT" , "w" );
printf ( "\n" );
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
printf ( "%2d" , bestRobots[i][j] );
fprintf ( file,"%2d" , bestRobots[i][j] );
}
printf ( "\n" );
fprintf (file, "\n" );
}
fclose ( file );
}
//检查是否全部被监视
bool checkAllRooms ()
{
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
if (monitor[i][j] == 0)
{
return false;
}
}
}
return true;
}
//剪枝函数,是否可以不放
bool canKeepUnsafe ( int x , int y )
{
if (monitor[x][y - 1] == 0)
return false;
if (y==n)
{
if (y > 0 && monitor[x][y - 1] == 0)
return false;
if (y = n && monitor[x][y] == 0)
return false;
}
}
//安排机器人监视陈列室
void arrangeRobots ( int x , int y , int robot )
{
if (robot > best)//剪枝
return;
if (x == m + 1)//安排完了所有的陈列室
{
if (checkAllRooms () && robot < best)//如果所有的陈列室都被监控且所用的机器人数少于之前的最小机器人数
{
best = robot;
memcpy ( bestRobots , robots , sizeof ( robots ) );//将当前的机器人摆放情况复制存入bestRobots
}
return;
}
//如果可以摆放机器人
if(monitor[x][y]==0&& monitor[x-1][y] == 0 && monitor[x][y-1] == 0 &&
monitor[x][y+1] == 0 && monitor[x+1][y] == 0 )
{
//在这里摆放一个机器人
robots[x][y] = 1;
robot++;
monitor[x][y] = 1;
monitor[x-1][y] = 1;
monitor[x][y-1] = 1;
monitor[x+1][y] = 1;
monitor[x][y+1] = 1;
//遍历下一个陈列室
if (y < n)
{
arrangeRobots ( x , y + 1 , robot );
}
else
{
arrangeRobots ( x + 1 , 1 , robot );
}
//回溯,撤销刚才摆放的机器人
robots[x][y] = 0;
robot--;
monitor[x][y] = 0;
monitor[x - 1][y] = 0;
monitor[x][y - 1] = 0;
monitor[x + 1][y] = 0;
monitor[x][y + 1] = 0;
}
//不摆,直接考虑下一个陈列室
if (canKeepUnsafe)//剪枝,如果可以不摆的话
{
if (y < n)
{
arrangeRobots ( x , y + 1 , robot );
}
else
{
arrangeRobots ( x + 1 , 1 , robot );
}
}
}
//用来测试性能的函数
void test (int x,int y)
{
for (int i = x; i <= y; i++)
{
for (int j = x; j <= y; j++)
{
m = i;
n = j;
initData ();
arrangeRobots ( 1 , 1 , 0 );
if (best == INT_MAX)//如果最终best仍然为INT_MAX表示没有方案
{
printf ( "%2d%2d -1\n" , m , n );
}
else
{
printf ( "%2d%2d%2d\n" , m , n , best );
}
}
}
}
int main ()
{
readFile ();
//用来测试算法性能
//clock_t start_clock = clock ();
//test ( 1 , 8 );
//clock_t end_clock = clock ();
//double passing_seconds = double ( end_clock - start_clock ) / CLOCKS_PER_SEC;
//printf ( "total time:%fms\n" , passing_seconds );
initData ();
arrangeRobots ( 1 , 1 , 0 );
if (best == INT_MAX)//如果最终best仍然未INT_MAX表示没有方案
{
printf ( "no solution!" );
}
else
{
printf ( "最优安排的机器人数是:%d" , best );
printArrange ();
}
return 0;
}
实验结果: