★问题描述:宝石排列问题
现有n种不同形状的宝石,每种n颗,共n²颗。同一种形状的n颗宝石分别具有n种不同的颜色c1,c2,...cn中的一种颜色。欲将这n²颗宝石排列成n行n列的一个方阵,使方阵中每一行和每一列的宝石都有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]代表方阵按从左到右、从上到下的顺序位置。用数字1—n*n代表n*n颗宝石。对每一个an[i],我们赋予任意an[i]两两不同的1—n*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/3、2/3与3/3是不同的形状,即有1/3-2/3、3/3-5/3、6/3-8/3、9/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();
}
原创,转载请声明