双向BFS—>NOIP2002 字串变换
如果目标也已知的话,用双向BFS能很大提高速度
单向时,是 b^len的扩展。
双向的话,2*b^(len/2) 快了很多,特别是分支因子b较大时
至于实现上,网上有些做法是用两个队列,交替节点搜索 ×,如下面的伪代码:
while(!empty())
{
扩展正向一个节点
遇到反向已经扩展的return
扩展反向一个节点
遇到正向已经扩展的return
}
但这种做法是有问题的,如下面的图:

求S-T的最短路,交替节点搜索(一次正向节点,一次反向节点)时
Step 1 : S –> 1 , 2
Step 2 : T –> 3 , 4
Step 3 : 1 –> 5
Step 4 : 3 –> 5 返回最短路为4,错误的,事实是3,S-2-4-T
我想,正确做法的是交替逐层搜索,保证了不会先遇到非最优解就跳出,而是检查完该层所有节点,得到最优值。
也即如果该层搜索遇到了对方已经访问过的,那么已经搜索过的层数就是答案了,可以跳出了,以后不会更优的了。
当某一边队列空时就无解了。
优化:提供速度的关键在于使状态扩展得少一些,所以优先选择队列长度较少的去扩展,保持两边队列长度平衡。这比较适合于两边的扩展情况不同时,一边扩展得快,一边扩展得慢。如果两边扩展情况一样时,加了后效果不大,不过加了也没事。
无向图时,两边扩展情况类似。有向图时,注意反向的扩展是反过来的 x->y(NOIP2002G2字串变换)
/*
双向BFS:
1.正向搜索:从初始结点向目标结点方向搜索,按照正向规则(A$->B$)变换。
2.逆向搜索:从目标结点向初始结点方向搜索,按照逆向规则(B$->A$)变换。
当两个方向的搜索生成同一子结点时终止此搜索过程(变换的总步数为此时两个方向BFS的步数总和)。 双向搜索通常有两种方法:
1. 两个方向交替扩展。
2. 选择结点个数较少的那个方向先扩展。
方法2克服了两方向结点的生成速度不平衡的状态,明显提高了效率。本程序使用方法1,两个方向交替BFS,进行正反规则变换。
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
struct Node
{
char str[41];
int sep;
Node()
{
sep=0;
memset(str,sizeof(str),0);
} }q1[20000], q2[20000]; int h1, r1, h2, r2; //h1,r1...h2,r2,分别保存起始和目标两个状态的队列头和尾
char s1[6][25], s2[6][25]; //存储变换规则 int n,Min=100; //Min储存最少规则变换次数 void copy2(int start, int use) //逆向搜索,当q2[]搜索到逆向规则匹配的字串B$的时候,进行替换
{
int i, j;
r2++; //队列中元素增加1个字串
q2[r2].sep = q2[h2].sep + 1;
for(i = 0; i < start; i++) //替换匹配规则的字串
{
q2[r2].str[i] = q2[h2].str[i];
}
for(j = 0; s1[use][j] != '\0'; j++, i++)
{
q2[r2].str[i] = s1[use][j];
}
for(j = start + strlen(s2[use]); q2[h2].str[j] != '\0'; j++, i++)
{
q2[r2].str[i] = q2[h2].str[j];
}
//cout<<"q2:"<<q2[r2].str<<endl;
for(i = 0; i <= r1; i++) //判断这一次规则替换操作结束后,正向BFS队列中是否会有字串元素与之匹配
{
if(strcmp(q1[i].str, q2[r2].str) == 0)
{
printf("%d\n", q1[i].sep + q2[r2].sep);
exit(0);
// if(q1[i].sep+q2[r2].sep<Min) Min=q1[i].sep+q2[r2].sep;
}
}
} void copy1(int start, int use) //正向搜索,当q1[]搜索到与规则匹配的字串A$的时候,进行替换
{
int i, j;
r1++;
q1[r1].sep = q1[h1].sep + 1; //队列q1[]中增加1个新的元素(一次规则变换后的字串)
for(i = 0; i < start; i++) //规则替换操作
{
q1[r1].str[i] = q1[h1].str[i];
}
for(j = 0; s2[use][j] != '\0'; j++, i++)
{
q1[r1].str[i] = s2[use][j];
}
for(j = start + strlen(s1[use]); q1[h1].str[j] != '\0'; j++, i++)
{
q1[r1].str[i] = q1[h1].str[j];
}
//cout<<"q1:"<<q1[r1].str<<endl;
for(i = 0; i <= r2; i++) //判断这一次规则替换操作结束后,方向BFS队列中是否会有字串元素与之匹配
{
if(strcmp(q2[i].str, q1[r1].str) == 0)
{ printf("%d\n", q2[i].sep + q1[r1].sep);
exit(0);
// if(q2[i].sep+q1[r1].sep<Min) Min=q2[i].sep+q1[r1].sep;
}
}
} void work(void)
{
int i, j;
while(h1 <= r1 && h2 <= r2) //搜索过程中确保没有一个队列为空,否则搜索不到相交的情况
{ if(q1[h1].sep + q2[h2].sep > 10) //正反搜索的步数总和超过了10,说明这样的转换至少要超过10次才能实现,结束
{
printf("NO ANSWER!\n");
exit(0);
} for(i = 0; i < strlen(q1[h1].str); i++)
{
for(j = 0; j < n; j++) //正向搜索,一共n个变换规则
{
if(strncmp(s1[j], &q1[h1].str[i], strlen(s1[j])) == 0)
{
copy1(i, j);
}
}
}
h1++; //正向一遍BFS,搜索完所有规则之后,队首元素出队 for(i = 0; i < strlen(q2[h2].str); i++) //加快搜索的速度,同理从目标开始,方向,并根据逆向规则进行BFS
{
for(j = 0; j < n; j++)
{
if(strncmp(s2[j], &q2[h2].str[i], strlen(s2[j])) == 0)
{
copy2(i, j);
}
}
}
h2++;
}
} int main()
{
freopen("in.in", "r", stdin);
// freopen("string.out", "w", stdout);
scanf("%s%s", q1[0].str, q2[0].str);
while(scanf("%s%s", s1[n], s2[n]) == 2)
{
n++;
}
work();
printf("NO ANSWER!\n");
return 0;
}
双向BFS—>NOIP2002 字串变换的更多相关文章
- NOIP2002 字串变换题解(双向搜索)
65. [NOIP2002] 字串变换 时间限制:1 s 内存限制:128 MB [问题描述] 已知有两个字串A$, B$及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ ...
- [NOIP2002]字串变换 T2 双向BFS
题目描述 已知有两个字串 A,B 及一组字串变换的规则(至多6个规则): A1−>B1 A2−>B2 规则的含义为:在 A$中的子串 A1可以变换为可以变换为B1.A2可以变换为可 ...
- NOIP2002字串变换[BFS]
题目描述 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为:在 A$中的子串 A1$ 可以变换为 B1$.A2 ...
- NOIP2002 字串变换
题二 字串变换 (存盘名: NOIPG2) [问题描述]: 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为: ...
- [NOIP2002] 字串变换 宽搜+深度优化
这道题硬是让我用STL水过.......而且题解里说的什么双向宽搜,交替扩展............... 这道题反正,STL用就用吧,但是状态数可以卡到千亿级别,因为这个东西是阶乘扩展的,然后我们发 ...
- [COGS 0065][NOIP 2002] 字串变换
65. [NOIP2002] 字串变换 ★★ 输入文件:string.in 输出文件:string.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] 已知有两个字 ...
- 「NOIP2002」「Codevs1099」 字串变换(BFS
1099 字串变换 2002年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 已知有两个字串 $A$, ...
- [NOIP2002] 提高组P1032 字串变换
题目描述 已知有两个字串 A, B 及一组字串变换的规则(至多6个规则): A1 -> B1 A2 -> B2 规则的含义为:在 A$中的子串 A1 可以变换为 B1.A2 可以变换为 B ...
- P1032 字串变换 字符串BFS
题目描述 已知有两个字串A,BA,B及一组字串变换的规则(至多66个规则): A_1A1 ->B_1B1 A_2A2 -> B_2B2 规则的含义为:在 AA中的子串 A_1A1 ...
随机推荐
- 从零开始 DOM操作 笔记
<div id="box" class="box"></div> --> var myBox = document.g ...
- logging模板日志格式
logging模板日志格式 创建loginfo.py模块,然后导入定义的logging配置,即可使用 cat loginfo.py """ logging配置 " ...
- 函数sigsuspend
sigqueue函数原型: 函数作用:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用 int sigqueue(pid_t pid, int si ...
- Senparc.Weixin微信开发(3) 自定义菜单与获取用户组
自定义菜单 代码参考:http://www.cnblogs.com/szw/p/3750517.html 还可以使用他们官网的自定义:https://neuchar.senparc.com/User/ ...
- C#矩阵求逆
来源:http://zhidao.baidu.com/link?url=DiqAbq9YUYn3z7QjxGGoF0PLZwN-Y9ecqKB7Gy38JWRD1riMIYukVKXKq88pxtWL ...
- csv导入数据到mongodb3.2
mongoimport.exe -d paper -c paper K:\paper.csv --type csv -f id,name 数据库名 表名 文件所在位置 文件类型 ...
- C# 之 比较两个word文档的内容
利用 Microsoft.Office.Interop.Word 组件进行比较.引入命名空间:using Word2013 = Microsoft.Office.Interop.Word; 代码如下: ...
- Spring boot自定义启动字符画(banner)
spring boot项目启动时会打印spring boot的ANSI字符画,可以进行自定义. 如何自定义 实现方式非常简单,我们只需要在Spring Boot工程的/src/main/resourc ...
- device not found解决方案
今天在使用cordova tools进行真机调试的,报了一个错:device not found; 字面意思就是没有发现设备,这时候可以首先尝试重启adb服务 C:\Users\username\Ap ...
- spark操作Kudu之读 - 使用DataFrame API
虽然我们可以通过上面显示的KuduContext执行大量操作,但我们还可以直接从默认数据源本身调用读/写API. 要设置读取,我们需要为Kudu表指定选项,命名我们要读取的表以及为表提供服务的Kudu ...