[DFS] P1092 虫食算(提高+

探讨了在不同进制下解决加法问题的算法,包括如何通过搜索找到正确的数字映射,以及如何使用剪枝策略减少搜索空间。介绍了Next数组的概念及其在问题求解中的应用。

Date:2019/10/19
Degree of difficulty:提高+
Original question:P1092 虫食算(提高+

Hi,又见面啦~
在这里插入图片描述
在这里插入图片描述
→ B e g i n H a p p i l y \to Begin Happily BeginHappily
首先看到这个题,还是被进制的外表吓到了,其实分析还是能得到一些东西的

  1. 怎样输入
  2. 搜索什么
  3. 怎么剪枝

怎样输入

因为我对字符串这块还是不是很熟悉,所以这里需要强调一下

搜索什么

n 进制的加法就是在十进制的基础上满十进一改成满 n 进一
由于这道题只考虑加法,所以进位只可能是 1 ,证明小学生都会,略
搜索的大体思路就是从第 1 位的值开始搜,搜到最后一位,判断是否合法

怎样剪枝

原作:

作者: zzlzk 更新时间: 2017-08-22 15:37

  1. 最高位不能有进位
  2. 对于每一位,a数组加上b数组%n是等于c数组的(或a+b+1%n)

讲解
在这里插入图片描述
假设这是十进制下的加法,怎么判断这个竖式对不对?

显然这个竖式是错误的,因为个位上 ( 8 + 6 ) m o d 10 = 4 ≠ 5 (8+6) mod 10=4\not =5 (8+6)mod10=4=5
由此推广到每一位,但是还要考虑进位,不慌,看另一张图

qwq在这里插入图片描述

这个竖式是对的还是错的?
这并不好判断,虽然 ( 8 + 6 ) m o d 10 = 4 ≠ 5 (8+6) mod 10=4 \not=5 (8+6)mod10=4=5但是这是中间位,有可能有进位
如果有进位, 那么 $(8+6+1) mod 10=5 $,这一位就是合法的了。

玄学——Next数组

这里会用到一个next数组
要手动模拟一下

AC code

//Author:PhilFan;
#include<bits/stdc++.h>
#include<cstdlib>
#include<cstring>
using namespace std;
#define maxn 30
char 	s1[maxn],s2[maxn],s3[maxn];
int 	a[maxn],b[maxn],c[maxn];		//上面两行是用来存输入的字母的 
int 	next[maxn];						//玄学数组 
int 	n,cnt,num[maxn];				
bool 	used[maxn];						//存放用了的数 
//函数部分------------------------------------------------------------------ 
inline int change(char c){ 	//1.将字符转化为数字 
	return c-'A';
}
void print(){				//2.输出函数 
	for(int i = 0; i < n; i++){
		printf("%d ",num[i]);
	}
	exit(0);			//结束程序,在cstdlib库中 
} 
bool judge(){				//剪枝1 判断每一位是否都合法 
	for(int i = n-1,x=0; i >= 0;i--){
		int A = num[a[i]],B = num[b[i]],C = num[c[i]];
		if( ((A + B + x)%n) != C)	return false;
		x = (A+B+x)/n;
	} 
	return true;
}
bool prune(){				//剪枝2 判断当前位是否合法,%n和+1%n结果是不是此处得数 
	if(num[a[0]]+num[b[0]]>=n)	return true;
	for(int i = n-1; i >= 0; i--){
		int A = num[a[i]],B = num[b[i]],C = num[c[i]];
		if(A==-1||B==-1||C==-1)	continue;
		if( ((A + B + 1)%n) != C && ((A+B)%n)!=C ){
			return true;
		}
	}
	return false; 
}
void getnext(int x){		//3.生成用的
	if(!used[x]){
		used[x] = true;
		next[cnt++]=x;
	}
	return;
}
void dfs(int t){			//4. 搜索 产生1~n-1的全排列 
	if(prune()==true)	return;			//判断当前位,剪枝 
	if(t==n){
		if(judge()==true)	print();	//判断所有,输出 
		return;
	}
	for(int i = n-1; i >= 0; i--){		//递归循环部分 
		if(!used[i]){
			num[next[t]] = i;
			used[i] = 1;
			dfs(t+1);
			used[i] = 0;
			num[next[t]] = -1;
		}
	} 
	return;
}
//主函数部分------------------------------------------------------------------ 
int main()
{
	scanf("%d",&n);
	scanf("%s%s%s",s1,s2,s3);		//将输入的字母存到字符数组中去 
	for(int i = 0; i < n; i++){		//把字母变成数字 
		a[i]=change(s1[i]); 
		b[i]=change(s2[i]);
		c[i]=change(s3[i]);//把字符数组变成数字 
		num[i]=-1;//用于dfs里,是每个字母代表的值 
	}
	for(int i = n-1; i>=0;i--){		//Next 数组的应用,玄学东西,不是很懂 
		getnext(a[i]);
		getnext(b[i]);
		getnext(c[i]);
	}
	memset(used,0,sizeof(used));	//把getnext数组中的用过的used数组清零 
	dfs(0);							//开始搜索 
	return 0;
}

手动模拟过程

经过字母变数字和next玄学数组的处理后,我们的处理结果是这样的
在这里插入图片描述
next是怎样出来的呢,

for(int i = n-1; i>=0;i--){		//Next 数组
		getnext(a[i]);
		getnext(b[i]);
		getnext(c[i]);
	}

这三行代码的过程是,我们来看
在这里插入图片描述
从右上角开始,把出现过的数字依次记录下来;
在这里插入图片描述
代表的字母分别是D E A C B

→ H a p p y E n d i n g \to Happy Ending HappyEnding

03-08
### 深度优先搜索 DFS C++ 实现教程 #### 使用栈实现深度优先搜索 深度优先搜索(Depth First Search,DFS)是一种图遍历法,它从一个节点开始,通过访问其相邻节点的方式,依次深入到图中的更深层次。Stack(栈)作为一种先进后出(Last In First Out,LIFO)的数据结构非常适合作为辅助工具来实现DFS法[^1]。 下面是一个基于栈的DFS实现例子: ```cpp #include <iostream> #include <vector> #include <stack> using namespace std; void dfs(const vector<vector<int>>& graph, int startVertex) { vector<bool> visited(graph.size(), false); stack<int> vertexStack; vertexStack.push(startVertex); while (!vertexStack.empty()) { int currentVertex = vertexStack.top(); vertexStack.pop(); if (!visited[currentVertex]) { cout << "Visited node: " << currentVertex << endl; visited[currentVertex] = true; for (int neighbor : graph[currentVertex]) { if (!visited[neighbor]) vertexStack.push(neighbor); } } } } ``` 这段代码展示了如何利用栈来进行非递归形式的DFS操作。对于每一个顶点,在将其标记为已访问之前先打印出来;接着把所有未被访问过的邻接点压入栈中等待处理。 #### 字符串排列组合问题下的DFS应用实例 另一个有趣的场景是在字符串上执行大小写转换的所有可能情况枚举。这里给出了一种解决方案,该方案采用递归方式实现了DFS逻辑[^2]: ```cpp #include <vector> #include <string> #include <cctype> using namespace std; vector<string> ans; // 记录最终结果 void DFS(int cur, string& s){ if(cur == s.size()){ ans.push_back(s); return ; } if(isdigit(s[cur])){ DFS(cur + 1, s); } else{ s[cur] = tolower(s[cur]); DFS(cur + 1, s); s[cur] = toupper(s[cur]); DFS(cur + 1, s); } } vector<string> letterCasePermutation(string S) { DFS(0, S); return ans; } ``` 此版本的`letterCasePermutation()`函数接收输入字符串S,并调用内部定义的帮助方法`DFS()`完成具体工作。每当遇到字母字符时就会创建两个新的路径分支——分别对应于当前位的小写字母和大写字母状态。 #### 组合问题求解中的DFS运用 除了上述两种典型的应用之外,DFS还可以用来解决各种类型的组合优化问题。例如给定一组候选数以及目标数值T,寻找能够加总得到T的不同子集数量等问题都可以借助DFS的思想得以高效解答[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值