万圣节后的早晨&&九数码游戏——双向广搜
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 ;
}
万圣节后的早晨&&九数码游戏——双向广搜的更多相关文章
- [ZJOI2005]九数码游戏
[ZJOI2005]九数码游戏 题目描述 输入输出格式 输入格式: 输入文件中包含三行三列九个数,同行的相邻两数用空格隔开,表示初始状态每个方格上的数字.初始状态不会是目标状态. 输出格式: 如果目标 ...
- 双向广搜+hash+康托展开 codevs 1225 八数码难题
codevs 1225 八数码难题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Yours和zero在研究A*启 ...
- 【双向广搜+逆序数优化】【HDU1043】【八数码】
HDU上的八数码 数据强的一B 首先:双向广搜 先处理正向搜索,再处理反向搜索,直至中途相遇 visit 和 队列都是独立的. 可以用一个过程来完成这2个操作,减少代码量.(一般还要个深度数组) 优化 ...
- HDU--杭电--1195--Open the Lock--深搜--都用双向广搜,弱爆了,看题了没?语文没过关吧?暴力深搜难道我会害羞?
这个题我看了,都是推荐的神马双向广搜,难道这个深搜你们都木有发现?还是特意留个机会给我装逼? Open the Lock Time Limit: 2000/1000 MS (Java/Others) ...
- 双向广搜 POJ 3126 Prime Path
POJ 3126 Prime Path Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 16204 Accepted ...
- 双向广搜 codevs 3060 抓住那头奶牛
codevs 3060 抓住那头奶牛 USACO 时间限制: 1 s 空间限制: 16000 KB 题目等级 : 黄金 Gold 题目描述 Description 农夫约翰被告知一头逃跑奶牛 ...
- nyoj 523 双向广搜
题目链接: http://acm.nyist.net/JudgeOnline/problem.php?pid=523 #include<iostream> #include<cstd ...
- poj 3131 Cubic Eight-Puzzle 双向广搜 Hash判重
挺不错的题目,很锻炼代码能力和调试能力~ 题意:初始格子状态固定,给你移动后格子的状态,问最少需要多少步能到达,如果步数大于30,输出-1. 由于单向搜索状态太多,搜到二十几就会爆了,所以应该想到双向 ...
- Eleven puzzle_hdu_3095(双向广搜).java
Eleven puzzle Time Limit: 20000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...
随机推荐
- R小问题
步骤 > library(xlsx) > test<-read.csv("I:/山农大学大数据中心/柱状图/z7.csv") > data1=test[] ...
- json简单操作
通过内置的json模块对json数据进行编码 1.对数据进行编码(dumps) import json #使用dumps将python数据结构转换为json data = { , "name ...
- PHP精确到毫秒秒杀倒计时实例
精确到毫秒秒杀倒计时PHP源码实例,前台js活动展示倒计时,后台计算倒计时时间.每0.1秒定时刷新活动倒计时时间. PHP: // 注意:php的时间是以秒算.js的时间以毫秒算 // 设置时区 da ...
- docker简单使用+django+uwsgi+nginx项目部署
使用docker 搭建 centos7 环境: 主机环境:windows 10专业版 一.安装docker Hub.docker.com官网下载 docker for windows 安装完成后,任务 ...
- http-equiv=mobile-agent说明
Meta声明的格式:<meta http-equiv=”mobile-agent” content=”format=[wml|xhtml|html5]; url=url”> 比如: < ...
- 宿主机ssh免密登录docker容器
一.检查系统内核 二.安装docker 1.yum install docker -y 2.docker version #查看docker版本 3.syste ...
- Codeforces 552 E. Two Teams
E. Two Teams time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...
- Python基础灬异常
异常&异常处理 异常!=错误 在程序运行过程中,总会遇到各种各样的错误. 有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这种错误我们通常称之为bug,bug是必须修复的 ...
- 最强NLP模型-BERT
简介: BERT,全称Bidirectional Encoder Representations from Transformers,是一个预训练的语言模型,可以通过它得到文本表示,然后用于下游任务, ...
- centos7.2 apache开启.htaccess
打开httpd.conf(在那里? APACHE目录的CONF目录里面),用文本编纂器打开后,查找 (1) AllowOverride None 改为 AllowOverride All (2)去掉下 ...