用一维数组解决宝石排列问题

★问题描述:宝石排列问题

现有n种不同形状的宝石,每种n颗,共n²颗。同一种形状的n颗宝石分别具有n种不同的颜色c1,c2,...cn中的一种颜色。欲将这n²颗宝石排列成nn列的一个方阵,使方阵中每一行和每一列的宝石都有n种不同形状和n种不同颜色。试设计一个算法,计算出对于给定的n,有多少中不同的宝石排列方案。

 

★算法设计:对于给定的n,计算出不同的宝石排列方案数。

 

★数据输入:由文件input.txt给出输入数据。第1行有1个正整数n,0<n<9

 

★结果输出: 将计算的宝石排列方案数输出到文件outout.txt

 

输入文件示例                    输出文件示例

Input.txt  output.txt

1              1


设计分析

本题目与八皇后问题有类似之处,可以理解为八皇后问题的升级版。参考之前网上学习过的一篇八皇后问题的解题思想,给出了属于自己的解题思路。(八皇后问题思想参考:https://blog.csdn.net/u014082714/article/details/44901575

由题知给出的宝石有n*n颗,形状有n种,颜色有n,每两颗宝石之间的形状或颜色都不相同。为解决此问题,我们用一个一维数组an[n*n]代表n*n的方阵,数组an[1]an[n*n]代表方阵按从左到右、从上到下的顺序位置。用数字1n*n代表n*n颗宝石。对每一个an[i],我们赋予任意an[i]两两不同的1n*n之间的一个数值m。以取整(m/n)代表不同的形状、以取余(m%n)代表不同的颜色。

例如:输入值n=3。我们以an[1]an[9]代表方阵的9个位置,在这9个位置赋予1-9的值代表9颗宝石。

an[1]

an[2]

an[3]

an[4]

an[5]

an[6]

an[7]

an[8]

an[9]

 

 

 

 

第一个宝石以数字1来,1/3代表宝石1的形状、1%3代表宝石1的颜色;第二个宝石以数字2来,2/3代表宝石2的形状、2%3代表宝石2的颜色……

虽然每两个数字之间的取整或取余都不相同,但我们发现,1/32/33/3是不同的形状,即有1/3-2/33/3-5/36/3-8/39/3四种形状,为此我们要做出调整。

color=m%n;

if(color==0)//当取余为0时,m的形状shape值要减1

    shape=m/n-1;

else shape=m/n;

然后利用回溯,从an[1]开始赋值,调用判断函数判断赋予的值是否符合条件,如果返回值为true,则开始an[2]的赋值……一直到为an[n*n]赋值。在整个数组an[]赋值完成后,计算可行解数的自增sum++。然后返回上层到an[n*n-1],对an[n*n-1]赋予其它可行性解,若有可行性解,则再对an[n*n]赋值,否则,再返回上层对an[n*n-2]赋予其它可行性解……直到整个回溯过程完成,输出可行解总数sum

判断函数:因为我们是按顺序赋予数组的值。所以我们在判断同行同列是否存在同形状或同颜色时,只需要往左与同行对比,往上与同列对比即可。

#include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
int n;
int sum=0;
int an[100]={0};
//此题与八皇后问题相似,可以说是八皇后的升级版,我们可以用与八皇后相似的回溯

/*我们可以用一维数组解决,共按棋盘从左到右,从上到下的顺序放置n*n个数
放置的数m取模(m/n)表示放置的形状,取余(m%n)表示放置的颜色
 */

//判断放m的时候颜色和形状跟前面的是否有冲突,有返回false,无返回true。你需要往前对比同行的,和往上对比同列的。
bool judge(int m,int i)
{
	int color,shape;						   	 
	color=m%n;		//m确定则颜色形状也确定	
	if(color==0)
		shape=m/n-1;	
	else shape=m/n;
									
	//往前对比同行
	int x=i%n,k=1;								 	
	if(x==0)								  
		x=n;
	while(k<x)
	{
		if(color==an[i-k]%n)
			return false;
		if(an[i-k]%n==0)
		{
			if(shape==an[i-k]/n-1)
				return false;
		}
		else if(shape==an[i-k]/n)
			return false;
		k++;
	}
	//往上对同列
	int l=i-n;
	while(l>0)
	{
		if(color==an[l]%n)
			return false;
		if(an[l]%n==0)
		{
			if(shape==an[l]/n-1)
				return false;
		}
		else if(shape==an[l]/n)
				return false;
		l -= n;
	}
	return true;
}											  

//回溯函数,从第一个(即an[1])开始赋予一个数
void huisu(int i)
{
	int m=1;
	if(i>n*n)//如果所有宝石已排完
	{
		sum++;
		/*if(sum<6)//输出5个例子示例
		{
			for(int b=1;b<=n*n;b++)
			{
				cout<<an[b]<<setw(3);
				if(b%n==0)
					cout<<endl;
			}
			cout<<endl;
		}*/
	}
	else
		for(m=1;m<=n*n;m++)
		{		
			int flag=1,j=1;
			while(j<i)
			{
				if(an[j]==m)//判断m在前面是否已被使用
				{
					flag=0;//m已被使用
					break;
				}
				j++;
			}
			if(flag==1)//m没被使用
			{
				an[i]=m;
				//cout<<m;
				if(judge(m,i))//如果an[i]可以放m这个数,即进行an[i+1]
					huisu(i+1);
			}
		}

}

void main()
{
	//cin>>n;
	ifstream input("input.txt");
	ofstream output("output.txt");
	while(input>>n!=NULL)
	{
		huisu(1);
		output<<"当n为"<<n<<"时,可行解总数有:"<<sum<<"种"<<endl;
		sum=0;
	}
	//cout<<endl<<sum<<endl;

	input.close();
	output.close();
}

原创,转载请声明
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值