信息学奥赛一本通 1936:【06NOIP普及组】Jam的计数法 | 洛谷 P1061 [NOIP 2006 普及组] Jam 的计数法

【题目链接】

ybt 1936:【06NOIP普及组】Jam的计数法
洛谷 P1061 [NOIP 2006 普及组] Jam 的计数法

【题目考点】

1. 深搜回溯

【解题思路】

只能使用字母b,c,d,e,f,g,h,i,j,构建5位“数字”。考察从bdfij变为bdghi的过程:
先看第5位,j需要取下一个字符,而不存在j的下一个字符了,那么看前一位。
看第4位,i需要取下一个字符,如果第4位取j,由于从左到右要严格递增,则第5位无法再取字符。因此第4位无法取下一个字符,看前一位。
看第3位,f可以取下一个字符g。而后看第4位,g后面第一个字符是h。第5位是h后的第一个字符i。
考虑再下一个数字,应该是bdghj,接下来是bdgij,然后是bdhij。。。。
如果该问题是输出所有的从s字母到t字母的w位数字,则可以使用深搜回溯完成。搜索时,下一个字母要比上一个字母大。该过程是深搜求一个序列组合的过程。

#include <bits/stdc++.h>
using namespace std;
int s, t, w, num[30], ct; 
void dfs(int k, int st)
{
	if(k > w)
	{
		for(int i = 1; i <= w; ++i)
			cout << char(num[i]-1+'a');
		cout << endl;
		return;
	}
	for(int i = st; i <= t; ++i)
	{
		num[k] = i;
		dfs(k+1, i+1);
	}
}
int main()
{
	cin >> s >> t >> w;
	dfs(1, s);
	return 0;
}

num数组记录的是字母的编号,a为1,b为2,…,z为26。
现在是要输出给定数字后面的5个数字。输入字符串b,b[k-1]是第k个字符,其字母编号为b[k-1]-'a'+1

现在需要从深搜回溯的中间某状态出发,向后继续搜索,搜索得到5个结果。
需要根据输入的字符串b,去设置搜索过程中各变量的值,让搜索过程中各变量的值等同于上述代码运行到刚刚输出b字符串时的数值。
该过程为一个预先处理的过程,设bool类型量pre,初值为真,pre为真表示当前在进行预处理,使整个dfs过程达到刚好搜索输出b字符串的状态。
调用dfs(1),搜索确定第一个数的值,如果第1个数的值是b字符串第1个字符的编号b[0]-'a'+1,那么此时i循环到的值应该是b[0]-'a'+1
递归调用dfs(2),搜索确定第二个数的值,如果第2个数的值是b[1]-'a'+1,那么此时i循环到的值应该是b[1]-'a'+1

调用dfs(k)时,确定第k个数是b[k-1]-'a'+1,那么该次调用中i应该循环到b[k-1]-'a'+1。因此在dfs(k)中,for循环i的初值应该设为b[k-1]-'a'+1
k>w时,进入搜索的递归出口,此时应该结束预处理,将pre设为假。
而后就开始正常的搜索组合的过程。设计数变量ct计数输出数字的数量,每输出一个数字,计数增加1,当计数达到5时,结束搜索。

【题解代码】

解法1:深搜回溯
#include <bits/stdc++.h>
using namespace std;
int s, t, w, num[30], ct; 
bool pre = true;//pre:是否处于预处理的状态
string b;
void dfs(int k, int st)
{
	if(k > w)
	{
		if(pre)
		{
			pre = false;
			return;
		}
		if(ct < 5)
		{
			for(int i = 1; i <= w; ++i)
				cout << char(num[i]-1+'a');
			cout << endl;
			ct++;
		}
		return;
	}
	if(ct == 5)//已经完成输出,结束搜索 
		return;
	for(int i = pre ? b[k-1]-'a'+1 : st; i <= t; ++i)
	{
		num[k] = i;
		dfs(k+1, i+1);
	}
}
int main()
{
	cin >> s >> t >> w >> b;
	dfs(1, s);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值