【题目链接】
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;
}