九宫幻方题

全排列方法

  • 通过next_permutation函数生成1-9的全排列。
  • 将每个全排列数转换为3*3的方阵
  • 判断每个元素是否与所给的缺少方阵一样,一样则继续进行,不一样直接continue退出本次的全排列
  • 在上一步元素匹配的情况下,进行是否是幻方判断。
    完成统计工作
    代码如下:
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
//
int p[9];//随机生产排列数
int ans[3][3];//存储幻方结果
int a[3][3];//读入题目给的样例方
int b[3][3];//全排列元素
//数据输入初始化
void input_martix(int (&b)[3][3], int (&p)[9])//传入数组的引用
{
    int a;
   cout<<"please input your data  9 numbers"<<endl;
   for(int i=0;i<3;i++)
   for(int j=0;j<3;j++)
   {  
       cin>>a;
       b[i][j]=a;
   }
   cout<<"9 numbers has been input "<<endl;
   //输入P矩阵,便于参加全排列
for(int i=0;i<9;i++)
  p[i]=i+1;
}
//输出数组 验证一下
void ouput_martix(int (&b)[3][3],int (&p)[9])//传入数组的引用
{
    int a;
   cout<<"the follow is the  9 numbers"<<endl;
   for(int i=0;i<3;i++)
   {
       for(int j=0;j<3;j++)
   {  
       
      cout<< b[i][j]<<"  ";
     
   }
   cout<<"  "<<endl;
   } 
   cout<<"P martix is   "<<endl;
   for(int i=0;i<9;i++)
   cout<<p[i]<<"  "<<endl;
}

signed main()
{
input_martix(a,p);
ouput_martix(a,p);

int cnt=0;
//将全排列数组转换为矩阵
do {
   for(int i=0;i<3;i++)
   {
       for(int j=0;j<3;j++)
      {
           b[i][j]=p[i*3+j];
    
      }

   }
   //判断生成的矩阵与题目所给的矩阵匹配与否
   bool flag=true;
    for(int i=0;i<3;i++)
   {
       for(int j=0;j<3;j++)
       {
           if(!a[i][j])
                {continue;}
            if(a[i][j]!=b[i][j])//如果匹配上了就将标志位设置为true
                flag = false; 
            //只要存在一个数不匹配就设置为false,且直接break退出
        }
    }
    

   //cout<<"flag is "<<flag;
    if(!flag)//如果返回的是false,则不可能存在匹配的幻方
        continue;
   bool ok= true;//判断是否是幻方的标志位
   int sum = b[0][0]+b[1][1]+b[2][2];
    //否则就有可能成为幻方,开始进行是否是幻方的判断
    if(sum !=b[0][2]+b[1][1]+b[2][0])//次对角线是否不相等
        continue;  
    //对比行和 与 列和是否匹配
    for(int i=0;i<3;i++)
   {  
       int tem1=0,tem2=0;
       for(int j=0;j<3;j++)
       {
           tem1+=b[i][j];tem2 += b[j][i];

       }     
             if(tem1!= sum || tem2 != sum)
              ok=false;
   }
   if(!ok) continue;
   cnt ++;
   if(cnt>=2) return cout<<"Too Many"<<endl,0;
    for(int i=0;i<3;i++)  for(int j=0;j<3;j++) ans[i][j]=b[i][j];//用ans 记录那个幻方

}while (next_permutation(p,p+9));
//程序到这里没有结束,则说明最少有一个幻方
cout<<"--------------------"<<endl;
for(int i=0;i<3;i++)  for(int j=0;j<3;j++) 
{
    //cout<<ans[i][j]<<"\n"[j==3];
    cout<<ans[i][j]<<"  \n";}//用ans 记录那个幻方

    return 0;
}

深度优先搜索方法

本意要求补全1——9的数字,使其填到对应位置上是否是形成幻方。本质就是对剩下的数字进行全排列。给的矩阵是

0 7 2
0 5 0 
0 3 0 

就是1,4,6,8,9还没有,只需要将1,4,6,8,9等数字填到原来的0 的位置,最后判断其是否是一个幻方即可。
那我们就对其全排列到对应位置。比如1,4,6,8,9 填到缺损位置 1,4,6,9,8填到缺损位置
补全之后然后判断是不是幻方即可完成任务。
(1)判断一个矩阵是不是幻方

bool check(int t[3][3])
{
    int sum = t[0][0]+t[1][1]+t[2][2];
    if(sum !=t[0][2]+t[1][1]+t[2][0])//次对角线是否不相等
    return false;
        for(int i=0;i<3;i++)
   {  
       int tem1=0,tem2=0;
       for(int j=0;j<3;j++)
       {
           tem1+=t[i][j];tem2 += t[j][i];

       }     
             if(tem1!= sum || tem2 != sum)
              return false;
   }
   return true;
}

(2)递归+回溯算法
回溯算法需要的参数可不像二叉树递归的时候那么容易一次性确定下来,所以一般是先写逻辑,然后需要什么参数,就填什么参数。
1,首先必须要有终止条件
在这里插入图片描述
本题中,我们知道我们就是求取1,4,6,8,9几个数的排列问题,将对应的数放在为0的位置上即可。
因此,我们就需要把所有的排列顺序算出来,再依次放到对应的位置上。
为了实现这个目标,我们需要将为0的位置记录下来,方便将排列放到对应的位置上。
为此,我们定义一个pair类型的数据

记录为0数据的位置,即x,y坐标
pair<int int> p[9]
在输入的时候,我们就记住这些为0的位置
、、
输入数据
、、
   for(int i=0;i<3;i++)
   for(int j=0;j<3;j++)
   {  
       cin>>a[i][j];
       if(!a[i][j])//如果给的元素是0的
        p[++n]=make_pair(i, j);//记录这个为0的元素x,y
        use[a[i][j]] = 1;//把元素输入矩阵里面有元素的地方标志为使用了
   }
   输入完毕之后,我们知道了p[1~5]5个数存入了为0的x,y数据。
     use数组里面,a[0]以及a[不为0的地方]=1,即a[2,3,5,7]都为1,记录使用过的数据

完成上诉之后,我们开始进行回溯算法的求解

int x=p[num].first,y=p[num].second;//每次num增加的时候,取对应的下标
for(int k=1;k<=9;k++)
{
   if(used[k]) continue;//如果数据已经使用过了,则不能再使用
   a[x][y]=k;
   used[k]=1;//表示该元素已经被使用了
   dfs( num+1, used);
   //回溯,完成一轮之后,把原来的位置给回归到上一步的样子
   a[x][y]=0;
   used[k]=0;
}
、、
说明:使用n来控制回溯的深度,因为有5个数据没有填,即n为5,num>5时候表示已经求出一个排列可能了

DFS算法的全部代码如下:

#include <algorithm>
#include <bits/stdc++.h>
#include <iostream>
#include <utility>
using namespace std;
int vis[10],a[3][3],ans[3][3];
int n=0;
int cnt=0;
pair<int,int> p[9];//存输入矩阵为0的下标
int use[10]={0};
//判断是否是幻方的矩阵
bool check(int t[3][3])
{
    int sum = t[0][0]+t[1][1]+t[2][2];
    if(sum !=t[0][2]+t[1][1]+t[2][0])//次对角线是否不相等
    return false;
        for(int i=0;i<3;i++)
   {  
       int tem1=0,tem2=0;
       for(int j=0;j<3;j++)
       {
           tem1+=t[i][j];tem2 += t[j][i];

       }     
             if(tem1!= sum || tem2 != sum)
              return false;
   }
   return true;
}

//DFS算法
void dfs(int num,int (&used)[10])
{
    if(num>n)//结束条件,表示幻方的缺失元素已经补全了
    {
        if(check(a))//如果是幻方
        {
            cnt++;
            for(int i=0;i<3;i++) 
            for(int j=0;j<3;j++)
            ans[i][j] = a[i][j];//将结果传给ans数组
        }
        return ;
    }
//
int x=p[num].first,y=p[num].second;//每次num增加的时候,取对应的下标
for(int k=1;k<=9;k++)
{
   if(used[k]) continue;//如果数据已经使用过了,则不能再使用
   a[x][y]=k;
   used[k]=1;//表示该元素已经被使用了
   dfs( num+1, used);
   //回溯
   a[x][y]=0;
   used[k]=0;
}
}

signed main()
{
   cout<<"please input your data  9 numbers"<<endl;
   for(int i=0;i<3;i++)
   for(int j=0;j<3;j++)
   {  
       cin>>a[i][j];
       if(!a[i][j])//如果给的元素是非0的
        p[++n]=make_pair(i, j);
        use[a[i][j]] = 1;//把元素输入矩阵里面有元素的地方标志为使用了
   }
cout<<"------";
dfs(1, use);
if (cnt>0)
cout<<"you huangfang"<<cnt;
else 
cout<<"meiyou huangfang";
    return 0 ;
}

-----------------2024年4月2号补,下面是对于这道题的接续思考-------------------------------
突然翻到了这道题,记得之前做过,今天又来做一下,从新复习一下DFS算法。

思路:找出幻方中没有使用的数,把他全排列填到对应的位置上,并判断是不是幻方即可。
为了方便,数独就先初始化一下:
int shudu[3][3]={
{0,7,2},
{0,5,0},
{0,3,0}
};//存储数独元素
std::vector<std::pair<int ,int> > tem;//存入原来数独里面是0的元素下标
int used[10]={0};//表示该数据是否用过

初始化一下

void Init() 
{
//	cout<<"---初始化完的数据是-- "<<endl;
	tem.clear();		
	for(int i =0;i<3;++i)
	for(int j =0;j<3;++j)
	{
		if(shudu[i][j]==0)
		{
			tem.push_back(std::make_pair(i,j));//记录每个0元素的下标 			
		} 
		else
		{	
			used[shudu[i][j]] = 1; //记录已经被使用的数据 
		}		
	}	 
}

检查是否是幻方

bool check(int s[3][3])
{
	int sum= s[0][0]+s[0][1]+s[0][2];
	//对角线是否相等
	if(sum != s[0][0]+s[1][1]+s[2][2] || sum != s[0][2]+s[1][1]+s[2][0]  ) 
		return false;
	
	for(int j=0;j<3;++j)
	{
		auto temp_row = s[j][0]+s[j][1]+s[j][2];//行和 
		auto temp_clo = s[0][j]+s[1][j]+s[2][j];//列和 
		if(sum  != temp_clo || sum  != temp_row )
		{
			return false;
		}
	}
	
return true;
}

DFS求全排列

void dfs(int n , int used[10],int s[3][3]) 
{

	//递归的停止条件
	if(path.size() == n) //找打了一组全排列
	{
			
			for(int i=0;i<n;++i)
			{
				s[tem[i].first][tem[i].second] = path[i]; //依次填到位置上
			}
		//	cout << "path_size= "<<path.size()<<endl;
			if ( check(s) )//如果是幻方 
		{
						count1=count1+1;
			cout<<"所求得的幻方是:"<<endl;
			for(int i =0;i<3;++i)
			{
				for(int j =0;j<3;++j)
				{
			 	 cout<< s[i][j]<<"  ";				
				}
				cout<<endl;	
			}		
		}
			return ;
	}
		
	//开是递归和回溯过程
	for(int i=1;i<=9;++i) 
	{
		if(used[i] != 0) continue;//这个数值已经用过了 
		
		path.push_back(i);
		used[i]=1;
		dfs(n,used,s);
		path.pop_back();
		//回溯
		used[i]=0; 
	}		
}

全部代码

//数独问题
#include<bits/stdc++.h>
using namespace std;
int count1 =0;
int shudu[3][3]={
				{0,7,2},
				{0,5,0},
				{0,3,0}
				};//存储数独元素

std::vector<std::pair<int ,int> > tem;//存入原来数独里面是0的元素下标 
std::vector<int> path;//存储每一轮的结果 
int used[10]={0};//表示该数据是否用过 
bool check(int s[3][3])
{
	int sum= s[0][0]+s[0][1]+s[0][2];
	//对角线是否相等
	if(sum != s[0][0]+s[1][1]+s[2][2] || sum != s[0][2]+s[1][1]+s[2][0]  ) 
		return false;
	
	for(int j=0;j<3;++j)
	{
		auto temp_row = s[j][0]+s[j][1]+s[j][2];//行和 
		auto temp_clo = s[0][j]+s[1][j]+s[2][j];//列和 
		if(sum  != temp_clo || sum  != temp_row )
		{
			return false;
		}
	}
	
return true;
}

void Init() 
{
//	cout<<"---初始化完的数据是-- "<<endl;
	tem.clear();		
	for(int i =0;i<3;++i)
	for(int j =0;j<3;++j)
	{
//		cout<<"hello world"<<endl;
		if(shudu[i][j]==0)
		{
			tem.push_back(std::make_pair(i,j));//记录每个0元素的下标 			
		} 
		else
		{	
			used[shudu[i][j]] = 1; //记录已经被使用的数据 
		}
			
			
	}
	
//	for(int i :used) 
//		cout<<"  used的值 "<<i<< "   " ;
//	 
}

void dfs(int n , int used[10],int s[3][3]) 
{

	//递归的停止条件
	if(path.size() == n) 
	{
			
			for(int i=0;i<n;++i)
			{
				s[tem[i].first][tem[i].second] = path[i];
			}
		//	cout << "path_size= "<<path.size()<<endl;
			if ( check(s) )//如果是幻方 
		{
						count1=count1+1;
			cout<<"所求得的幻方是:"<<endl;
			for(int i =0;i<3;++i)
		{
			for(int j =0;j<3;++j)
			{
			  cout<< s[i][j]<<"  ";
					
			}
			cout<<endl;	
		}		
		}
			return ;
	}
		
	//开是递归和回溯过程
	for(int i=1;i<=9;++i) 
	{
		if(used[i] != 0) continue;//这个数值已经用过了 
		
		path.push_back(i);
		used[i]=1;
		dfs(n,used,s);
		path.pop_back();
		//回溯
		used[i]=0; 
	}
	
	
}


signed main()
{

	Init();
	//cout<<"*******************************"<<endl;
	
	dfs(5 , used,shudu); 	
	//cout<<"cout1 = " <<count1<<endl; 
	//cout<< boolalpha << check(shudu) <<endl;
	return 0;
}

结果

在这里插入图片描述

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python九宫算数是一种基于数学的游戏,它的目标是填满一个3x3的方格,使得每行、每列和对角线上的数字之和都相等。这个相等的数字被称为“魔数”。 Python九宫算数的解法可以使用回溯算法,即从第一个格子开始,依次填入数字,如果填入的数字导致当前状态不符合要求,则回溯到上一个格子重新填入数字。直到所有格子都被填满,或者无法找到解。 以下是Python九宫算数的解法步骤: 1. 初始化一个3x3的方格,将所有格子都设置为0。 2. 从第一个格子开始,依次填入数字1-9。 3. 每填入一个数字,就检查当前状态是否符合要求。如果符合要求,则继续填下一个格子;否则回溯到上一个格子重新填入数字。 4. 当所有格子都被填满时,检查是否符合要求。如果符合要求,则输出结果;否则回溯到上一个格子重新填入数字。 以下是Python九宫算数的代码实现: ```python def solve_sudoku(grid): if is_complete(grid): return grid row, col = find_empty_cell(grid) for num in range(1, 10): if is_valid_move(grid, row, col, num): grid[row][col] = num if solve_sudoku(grid): return grid grid[row][col] = 0 return None def is_complete(grid): for row in range(9): for col in range(9): if grid[row][col] == 0: return False return True def find_empty_cell(grid): for row in range(9): for col in range(9): if grid[row][col] == 0: return row, col return None def is_valid_move(grid, row, col, num): for i in range(9): if grid[row][i] == num or grid[i][col] == num: return False box_row = (row // 3) * 3 box_col = (col // 3) * 3 for i in range(box_row, box_row + 3): for j in range(box_col, box_col + 3): if grid[i][j] == num: return False return True ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

书中藏着宇宙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值