https://www.luogu.org/problemnew/show/P1778

https://www.luogu.org/problemnew/show/P2578

双向广搜。

有固定起点终点,过程可逆。

有时用于A*估价函数不好用的时候。

万圣节后的早晨

(由于鬼可以同时移动,估价函数不好设计)

优化:

1.预处理。

预处理每个可以到的位置的上下左右的块能否到达,建一个类似前向星的数组。

就可以很快地查询了。

2.每次枚举前1/2个鬼就可以判定当前的状态是否合法,可以减掉不少。

尽可能提前判断合法性,越快越好。

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int N=;
const int M=;
const int P=;
int mp[N][N];
int d1[M][M][M];
int d2[M][M][M];
int id[N][N];
int to[M][],sz[M];
int tot;
int n,m,k;
char s[N];
struct node{
int mem[];
void clear(){
mem[]=mem[]=mem[]=mem[]=;
}
void op(){
cout<<mem[]<<" "<<mem[]<<" "<<mem[]<<endl;
}
}st,nd;
node unzip(ll x){
//cout<<" unzip "<<x<<endl;
node ret;
ret.clear();
int cnt=;
while(x){
cnt++;
ret.mem[cnt]=x%P;
x/=P;
}
return ret;
}
int mv[][]={{,},{+,},{-,},{,+},{,-}};
int zip(node lp){
return lp.mem[]*P*P+lp.mem[]*P+lp.mem[];
}
queue<pair<int,int> >q1;
queue<pair<int,int> >q2;
bool bfs1(int dep){
while(!q1.empty()){
pair<int,int>now=q1.front();
if(now.second==dep) return false;
q1.pop();
//cout<<" haha "<<endl;
node kk=unzip(now.first);
//if(dep<10) kk.op();
node lp;lp.clear();
for(int i=;i<=sz[kk.mem[]];i++){
lp.mem[]=to[kk.mem[]][i]; for(int j=;j<=sz[kk.mem[]];j++){
lp.mem[]=to[kk.mem[]][j];
if(lp.mem[]==lp.mem[]) continue;
if(lp.mem[]==kk.mem[]&&lp.mem[]==kk.mem[]) continue;
//cout<<"lp "<<endl;lp.op();
for(int p=;p<=sz[kk.mem[]];p++){
lp.mem[]=to[kk.mem[]][p];
if(lp.mem[]){
if(lp.mem[]==lp.mem[]) continue;
if(lp.mem[]==lp.mem[]) continue;
if(lp.mem[]==kk.mem[]&&lp.mem[]==kk.mem[]) continue;
if(lp.mem[]==kk.mem[]&&lp.mem[]==kk.mem[]) continue;
}
if(d2[lp.mem[]][lp.mem[]][lp.mem[]]!=-) return true;
else if(d1[lp.mem[]][lp.mem[]][lp.mem[]]==-){
d1[lp.mem[]][lp.mem[]][lp.mem[]]=dep;
q1.push(make_pair(zip(lp),now.second+));
}
}
}
}
}
return false;
}
bool bfs2(int dep){
while(!q2.empty()){
pair<int,int>now=q2.front();
if(now.second==dep) return false;
q2.pop();
node kk=unzip(now.first);
node lp;lp.clear();
for(int i=;i<=sz[kk.mem[]];i++){
lp.mem[]=to[kk.mem[]][i];
for(int j=;j<=sz[kk.mem[]];j++){
lp.mem[]=to[kk.mem[]][j];
if(lp.mem[]==lp.mem[]) continue;
if(lp.mem[]==kk.mem[]&&lp.mem[]==kk.mem[]) continue;
for(int p=;p<=sz[kk.mem[]];p++){
lp.mem[]=to[kk.mem[]][p];
if(lp.mem[]){
if(lp.mem[]==lp.mem[]) continue;
if(lp.mem[]==lp.mem[]) continue;
if(lp.mem[]==kk.mem[]&&lp.mem[]==kk.mem[]) continue;
if(lp.mem[]==kk.mem[]&&lp.mem[]==kk.mem[]) continue;
}
if(d1[lp.mem[]][lp.mem[]][lp.mem[]]!=-) return true;
else if(d2[lp.mem[]][lp.mem[]][lp.mem[]]==-){
d2[lp.mem[]][lp.mem[]][lp.mem[]]=dep;
q2.push(make_pair(zip(lp),now.second+));
}
}
}
}
}
return false;
}
void clear(){
tot=;
memset(mp,,sizeof mp);
memset(d1,-,sizeof d1);
memset(d2,-,sizeof d2);
memset(id,,sizeof id);
memset(to,,sizeof to);
memset(sz,,sizeof sz);
st.clear();
nd.clear();
while(!q1.empty())q1.pop();
while(!q2.empty())q2.pop();
}
int main(){
while(){
cin>>m>>n>>k;
if(n==&&m==&&k==) break;
clear();
char ch;while((ch=getchar())||)if(ch=='\n')break;
for(int i=;i<=n;i++){
int lp=;
while((s[lp]=getchar())||) if(s[lp++]=='\n')break;
for(int j=;j<=m;j++){
if(s[j-]==' '){
mp[i][j]=;
id[i][j]=++tot;
}
else if(s[j-]=='#'){
mp[i][j]=;
}
else if(s[j-]>='a'&&s[j-]<='z'){
id[i][j]=++tot;
st.mem[s[j-]-'a'+]=tot;
}
else if(s[j-]>='A'&&s[j-]<='Z'){
id[i][j]=++tot;
nd.mem[s[j-]-'A'+]=tot;
}
}
} for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
if(!id[i][j]) continue;
//cout<<i<<" "<<j<<endl;
for(int p=;p<;p++){
int dx=i+mv[p][];
int dy=j+mv[p][];
if(dx<||dx>n) continue;
if(dy<||dy>m) continue;
if(mp[dx][dy]) continue;
sz[id[i][j]]++;
to[id[i][j]][sz[id[i][j]]]=id[dx][dy];
}
}
} //cout<<"after "<<endl;
id[][]=;
sz[]++;
to[][]=;
//st.op();
q1.push(make_pair(zip(st),));
//cout<<zip(st)<<endl;
d1[st.mem[]][st.mem[]][st.mem[]]=;
q2.push(make_pair(zip(nd),));
d2[nd.mem[]][nd.mem[]][nd.mem[]]=;
int ans=;
int b1=,b2=;
while(){
ans++;
//if(ans<10) cout<<ans<<endl;
if(bfs1(++b1)) break;
ans++;
if(bfs2(++b2)) break;
}
cout<<ans<<endl;
}
return ;
}

九数码游戏

(由于顺时针转,变化太大,估价函数也不好设计。)

(当然这个题可以直接bfs,但是双向广搜在luogu上快了50倍)其实要注意的就是一点。

因为双向广搜的另一边是一个逆过程,所以,顺时针变成逆时针,向右变成向左。

值得注意。

至于方案,根据hash表中元素的单一性,而且有SPJ,所以,记录一下每个状态的前驱即可。

代码:

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=;
const int M=;
const int mo=1e5;
const int ED=;
struct ha{
int val[M];
int nxt[M],hd[mo];
int pre[M];
int cnt;
void ins(int x,int from){
cnt++;
val[cnt]=x;pre[cnt]=from;
int pos=x%mo;
nxt[cnt]=hd[pos];
hd[pos]=cnt;
}
bool query(int x){
int pos=x%mo;
for(int i=hd[pos];i;i=nxt[i]){
if(val[i]==x) return ;
}
return ;
}
int fin(int x){
int pos=x%mo;
for(int i=hd[pos];i;i=nxt[i]){
if(val[i]==x) return pre[i];
}
return ;
}
}HA1,HA2;
queue<pair<int,int> >q1;
queue<pair<int,int> >q2;
int st,nd;
int sta[],top;
int num1,num2;
int mp[][];
void op(int x){
for(int i=;i>=;i--)
for(int j=;j>=;j--)
mp[i][j]=x%,x/=;
for(int i=;i<=;i++){
for(int j=;j<=;j++){
printf("%d ",mp[i][j]);
}puts("");
}
puts("");
}
int mv1(int x){
for(int i=;i>=;i--)
for(int j=;j>=;j--)
mp[i][j]=x%,x/=;
int tmp=mp[][];
mp[][]=mp[][];mp[][]=mp[][];mp[][]=mp[][];mp[][]=mp[][];
mp[][]=mp[][];mp[][]=mp[][];mp[][]=mp[][];mp[][]=tmp;
int ret=;
for(int i=;i<=;i++){
for(int j=;j<=;j++){
ret=ret*+mp[i][j];
}
}return ret;
}
int mv2(int x){
for(int i=;i>=;i--)
for(int j=;j>=;j--)
mp[i][j]=x%,x/=;
int tmp=mp[][];
mp[][]=mp[][];mp[][]=mp[][];mp[][]=tmp;
int ret=;
for(int i=;i<=;i++){
for(int j=;j<=;j++){
ret=ret*+mp[i][j];
}
}return ret;
}
int mv3(int x){
for(int i=;i>=;i--)
for(int j=;j>=;j--)
mp[i][j]=x%,x/=;
int tmp=mp[][];
mp[][]=mp[][];mp[][]=mp[][];mp[][]=mp[][];mp[][]=mp[][];
mp[][]=mp[][];mp[][]=mp[][];mp[][]=mp[][];mp[][]=tmp;
int ret=;
for(int i=;i<=;i++){
for(int j=;j<=;j++){
ret=ret*+mp[i][j];
}
}return ret;
}
int mv4(int x){
for(int i=;i>=;i--)
for(int j=;j>=;j--)
mp[i][j]=x%,x/=;
int tmp=mp[][];
mp[][]=mp[][];mp[][]=mp[][];mp[][]=tmp;
int ret=;
for(int i=;i<=;i++){
for(int j=;j<=;j++){
ret=ret*+mp[i][j];
}
}return ret;
}
int bfs1(int dp){
while(!q1.empty()){
pair<int,int> lp=q1.front();
if(lp.second==dp) return ;
q1.pop();
int now=lp.first;
int t1=mv1(now);
if(HA2.query(t1)){
num1=now;
num2=t1;
return ;
}
else if(!HA1.query(t1)){
HA1.ins(t1,now);
q1.push(make_pair(t1,lp.second+));
} int t2=mv2(now);
if(HA2.query(t2)){
num1=now;
num2=t2;
return ;
}
else if(!HA1.query(t2)){
HA1.ins(t2,now);
q1.push(make_pair(t2,lp.second+));
}
}
return ;
}
int bfs2(int dp){
while(!q2.empty()){
pair<int,int> lp=q2.front();
if(lp.second==dp) return ;
q2.pop();
int now=lp.first;
int t1=mv3(now);
if(HA1.query(t1)){
num2=now;
num1=t1;
return ;
}
else if(!HA2.query(t1)){
HA2.ins(t1,now);
q2.push(make_pair(t1,lp.second+));
} int t2=mv4(now);
if(HA1.query(t2)){
num2=now;
num1=t2;
return ;
}
else if(!HA2.query(t2)){
HA2.ins(t2,now);
q2.push(make_pair(t2,lp.second+));
}
}
return ;
}
int main(){
int t;
for(int i=;i<=;i++)scanf("%d",&t),st=st*+t;
nd=ED;
if(st==nd){
printf("");
op(st);
}
HA2.ins(nd,);
HA1.ins(st,);
q1.push(make_pair(st,));
q2.push(make_pair(nd,));
int ans=;
int b1=,b2=;
while(){
ans++;
int d1=bfs1(++b1);
if(d1==) break;
ans++;
int d2=bfs2(++b2);
if(d2==) break;
if(d1==&&d2==) {
printf("UNSOLVABLE");return ;
}
}
//over
printf("%d\n",ans);
top=;
while(num1!=){
sta[++top]=num1;
num1=HA1.fin(num1);
}
while(top)op(sta[top--]);
while(num2!=){
op(num2);
num2=HA2.fin(num2);
}
return ;
}

万圣节后的早晨&&九数码游戏——双向广搜的更多相关文章

  1. [ZJOI2005]九数码游戏

    [ZJOI2005]九数码游戏 题目描述 输入输出格式 输入格式: 输入文件中包含三行三列九个数,同行的相邻两数用空格隔开,表示初始状态每个方格上的数字.初始状态不会是目标状态. 输出格式: 如果目标 ...

  2. 双向广搜+hash+康托展开 codevs 1225 八数码难题

    codevs 1225 八数码难题  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond   题目描述 Description Yours和zero在研究A*启 ...

  3. 【双向广搜+逆序数优化】【HDU1043】【八数码】

    HDU上的八数码 数据强的一B 首先:双向广搜 先处理正向搜索,再处理反向搜索,直至中途相遇 visit 和 队列都是独立的. 可以用一个过程来完成这2个操作,减少代码量.(一般还要个深度数组) 优化 ...

  4. HDU--杭电--1195--Open the Lock--深搜--都用双向广搜,弱爆了,看题了没?语文没过关吧?暴力深搜难道我会害羞?

    这个题我看了,都是推荐的神马双向广搜,难道这个深搜你们都木有发现?还是特意留个机会给我装逼? Open the Lock Time Limit: 2000/1000 MS (Java/Others)  ...

  5. 双向广搜 POJ 3126 Prime Path

      POJ 3126  Prime Path Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 16204   Accepted ...

  6. 双向广搜 codevs 3060 抓住那头奶牛

    codevs 3060 抓住那头奶牛 USACO  时间限制: 1 s  空间限制: 16000 KB  题目等级 : 黄金 Gold   题目描述 Description 农夫约翰被告知一头逃跑奶牛 ...

  7. nyoj 523 双向广搜

    题目链接: http://acm.nyist.net/JudgeOnline/problem.php?pid=523 #include<iostream> #include<cstd ...

  8. poj 3131 Cubic Eight-Puzzle 双向广搜 Hash判重

    挺不错的题目,很锻炼代码能力和调试能力~ 题意:初始格子状态固定,给你移动后格子的状态,问最少需要多少步能到达,如果步数大于30,输出-1. 由于单向搜索状态太多,搜到二十几就会爆了,所以应该想到双向 ...

  9. Eleven puzzle_hdu_3095(双向广搜).java

    Eleven puzzle Time Limit: 20000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

随机推荐

  1. Redis的数据类型以及每种数据类型的使用场景

    人就是很奇怪的动物,很简单的问题往往大家都容易忽略,当我们在使用分布式缓存Redis的时候,一个最简单的问题Redis的数据类型以及每种数据类型的使用场景是什么? 是不是觉得这个问题很基础?我也这么觉 ...

  2. ASP.NET MVC - 启动创建项目,未能加载错误

    VS2012以常规方式创建一ASP.NET MVC internet 项目.创建后F5启动项目,遇一错误: 未能加载文件或程序集“MySql.Web.v20, Version=6.9.4.0, Cul ...

  3. 遗传算法框架GAFT优化小记

    前言 前段时间一直在用自己写的遗传算法框架测试算法在优化力场参数的效果,但是跑起来效率很慢,因为适应度函数需要调用多次力场程序计算能量,但是还是比我预想中的慢我也没有及时对程序进行profiling和 ...

  4. loadrunner--基础2

    LR11-03 一.并发测试(n VU) 1.并发测试两个条件 1)脚本中要有 集合点(并发点) 2)控制台中要设置并发策略(选择第一项,所有虚拟用户到达集合点后释放) 集合点: 5个线程,代表5个V ...

  5. 用VS测试程序

    怀着一种忐忑的心情,我开始了我的软件测试. #include "stdio.h" #include "stdlib.h" int main(int argc, ...

  6. Oracle win64_12g 安装

    1.Oracle win64_12g 安装 1.下载安装包:这里需要自己注册一下,然后就可以登录下载软件了. 下载地址: http://www.oracle.com/technetwork/datab ...

  7. jQuery的2把利器

    <!-- $是一个函数,首先是给window添加$,然后该值是一个函数,函数返回的值是对象.1. jQuery核心函数 * 简称: jQuery函数($/jQuery) * jQuery库向外直 ...

  8. Scrum团队成立及《构建之法》第六、七章读后感

    5.Scrum团队成立 5.1 团队名称:喳喳      团队目标:突破渣渣      团队口号:吱吱喳喳      团队照: 5.2 角色分配 产品负责人: 112冯婉莹 Scrum Master: ...

  9. 2nd 词频统计更新

    词频统计更新 实现功能:从控制台输入文件路径,并统计单词总数及不重复的单词数,并输出所有单词词频,同时排序. 头文件 #include <stdio.h> #include <std ...

  10. c 结构体读取与保存

    1.结构体保存到文本 #include <stdio.h> #include <stdlib.h> #include <string.h> #define max ...