c语言-回溯法解决世界名画陈列馆问题(不重复监视)

问题描述:
世界名画陈列馆由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;
}

实验结果:
在这里插入图片描述

  • 10
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是用C语言实现世界名画陈列馆问题回溯法代码: ``` #include <stdio.h> #define MAXN 100 int n, k; int a[MAXN]; // 存储每个画家的位置 int used[MAXN]; // 标记画家是否已经被放置 void dfs(int cur) { if (cur == k) { // 所有画家都已经被放置 for (int i = 0; i < k; i++) { printf("%d ", a[i] + 1); // 输出每个画家的位置(注意要加1) } printf("\n"); return; } for (int i = 0; i < n; i++) { if (!used[i]) { // 如果这个位置还没有被占用 int ok = 1; for (int j = 0; j < cur; j++) { if (abs(i - a[j]) <= 1) { // 如果这个位置与已经放置的画家的位置相邻 ok = 0; break; } } if (ok) { // 如果这个位置可以放置画家 used[i] = 1; a[cur] = i; dfs(cur + 1); // 继续放置下一个画家 used[i] = 0; } } } } int main() { scanf("%d%d", &n, &k); dfs(0); // 从第一个画家开始放置 return 0; } ``` 代码中的 `dfs` 函数实现了回溯过程,其中 `cur` 表示当前放置的画家的数量, `a` 数组存储每个画家的位置, `used` 数组标记画家是否已经被放置。 在 `dfs` 函数中,首先判断是否所有画家都已经被放置,如果是,则输出结果并返回。否则,遍历每个位置,如果这个位置可以放置画家,则判断这个位置是否与已经放置的画家的位置相邻,如果不相邻,则将这个位置标记为已占用,将这个位置赋给当前的画家,继续放置下一个画家。如果这个位置不能放置画家,则继续遍历下一个位置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值