方格填数(递推dfs,状态编码)

题目

如下的10个格子
在这里插入图片描述
填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)

一共有多少种可能的填数方案?

题解

本题其实并不需要状态编码,因为从1行2列开始填和从1行3列开始填,所得到的状态是不一样的,不像 蓝桥杯的7段码 中状态会有重叠。写到一半才注意到,但是可以练练状态编码。
状态编码:
这里使用映射:map<pair<int,int>,int> mp,如1行1列映射为数字0,1行2列映射为数字1,1行3列映射为数字2,假如1行2列里填的数为0,1行3列里填的数字为1,那么得到的状态编码为0*10^1+1*10^2。如果访问到该状态,就将该状态加入到set集合中,代表已经访问过。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int my_mp[4][5];//1~3  1~4
int ans=0;
map<pair<int,int>,int> mp;
set<ll> my_st;
ll my_pow(int t)//10^t
{
	ll a=1;
	for(int i=0;i<t;i++)
	{
		a=a*10;
	}
	return a; 
}
ll convert()
{
	ll num=0;
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=4;j++)
		{
			if(my_mp[i][j]==-2) continue;
			num+=(ll)(my_mp[i][j])*my_pow(mp.find(make_pair(i,j))->second);
		}
	}
	return num;
}
void dfs(int row,int col,int num)
{
	my_mp[row][col]=num;
	//cout<<row<<" "<<col<<" "<<num<<endl;
	if(num==9) 
	{
		ll temp=convert();
		if(my_st.find(temp)==my_st.end())
		{
			ans++;
			my_st.insert(temp);
			//cout<<"符合条件的结果"<<temp;
		}
		//cout<<"结束----------------------------------------------------"<<endl; 
	}
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=4;j++)
		{	
			if(my_mp[i][j]==-2 || my_mp[i][j]>=0) continue;
			//cout<<"i"<<i<<"j"<<j;
			//cout<<"abs(i-row)"<<abs(i-row);
			//cout<<"abs(j-col)"<<abs(j-col)<<endl;
			if(abs(i-row)<=1 && abs(j-col)<=1) continue;
			dfs(i,j,num+1);
		}
	}
	my_mp[row][col]=-1;
}
void init()
{
	int s=0;
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=4;j++)
		{
			pair<int,int> p(i,j);
			mp[p]=s;
			s++;
		}
	}
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=4;j++)
		{
			my_mp[i][j]=-1;
		}
	}
	my_mp[1][1]=-2;
	my_mp[3][4]=-2;
}
int main()
{
	init();
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=4;j++)
		{
			if(my_mp[i][j]==-2) continue;
			//cout<<"-------------------------------------------------"<<endl;
			dfs(i,j,0);
		}
	} 
	cout<<ans;
}
### 栈和递归在深度优先搜索 (DFS) 中的应用 #### 使用递归实现 DFS 递归是一种直观的方式实现深度优先搜索。通过函自身的调用,程序能够自动维护当前节点及其子节点的信息。每次进入一个新的节点时,都会创建一个新的递归帧来存储该节点的相关据。这种机制非常适合于表示树形结构或图的遍历过程。 递归的核心在于其天然支持回溯操作。当某个路径被完全探索完毕后,递归会自动返回至上一层调用,从而允许算法尝试其他分支[^1]。这种方法的优点是代码简单易懂,缺点是在处理大规模或者深层嵌套的据结构时可能导致栈溢出问题[^2]。 #### 使用栈实现非递归 DFS 为了克服递归可能带来的性能瓶颈,可以采用显式栈的方式来模拟递归的过程。在这种方法中,手动管理一个堆栈以保存待访问的节点信息。初始状态下,将起始节点压入栈中;随后,在循环过程中不断弹出栈顶元素进行访问,并将其未访问过的邻居依次加入栈中[^4]。 这种方式不仅避免了因过多层次而导致的标准运行期栈空间耗尽的风险,还提供了更大的灵活性去调整具体的执行策略,比如改变节点选取顺序等[^3]。 #### 两者的主要区别 - **内存消耗**: 递归依赖系统提供的隐含调用栈,对于非常深的递归链来说可能存在风险;而非递归版本则由程序员控制外部据结构大小,理论上可适应任意规模输入。 - **效率考量**: 尽管现代编译器优化技术已经极大地提高了尾部递归场景下的表现,但对于一般情况而言,基于栈的手动迭代往往具有更高的计算效能。 - **编码难度**: 相较之下,利用内置语言特性完成的任务总是显得更加简洁明了,所以大多情况下人们更倾向于编写递归形式的解决方案除非遇到特殊需求限制条件之外的情况才会考虑转换成相应的非递归模式。 ```c // 非递归 C 实现示例 void DFS_NonRC(ALGraph G, int v) { Stack S; ArcNode *p; int k, w; InitStack(&S); Push(&S, v); while (!IsEmpty(S)) { Pop(&S, &k); if (!visited[k]) { visit(k); visited[k] = TRUE; p = G.vertices[k].firstarc; while (p != NULL) { w = p->adjvex; if (!visited[w]) Push(&S, w); p = p->nextarc; } } } } ``` 问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值