题目链接

紫书例题。

首先附上我第一次bfs+剪枝TLE的版本:

 #include<bits/stdc++.h>

 using namespace std;
typedef long long ll;
const int N=+,inf=0x3f3f3f3f;
const int b[][]= {
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
};
const int md[]= {,,,,,,,};
void rot(int* c,int x) {
const int* bb=b[x];
for(int i=; i<; ++i)swap(c[bb[i]],c[bb[i+]]);
}
void enc(int* c,ll& x) {
x=;
for(int i=; i>=; --i)x=x*+c[i];
}
void dec(int* c,ll x) {
for(int i=; i<; ++i)c[i]=;
for(int i=; i<; ++i)c[i]=x%,x/=;
}
int ok(int* c) {
for(int i=; i<; ++i)if(c[md[i]]!=c[md[i+]])return false;
return true;
}
map<ll,int> d;
vector<ll> t;
int c[N],cc[N],s[N],mi;
ll ss; void bfs1() {
mi=inf;
d.clear();
t.clear();
queue<ll> q;
q.push(ss),d[ss]=;
while(!q.empty()) {
ll u=q.front();
q.pop();
dec(c,u);
if(ok(c)) {
mi=min(mi,d[u]);
t.push_back(u);
}
if(d[u]>=mi)continue;
for(int i=; i<; ++i) {
memcpy(cc,c,sizeof c);
rot(cc,i);
ll v;
enc(cc,v);
if(!d.count(v)) {
d[v]=d[u]+;
q.push(v);
}
}
}
} void bfs2() {
d.clear();
queue<ll> q;
for(ll i:t)q.push(i),d[i]=;
while(!q.empty()) {
ll u=q.front();
q.pop();
if(d[u]==mi)continue;
dec(c,u);
for(int i=; i<; ++i) {
memcpy(cc,c,sizeof c);
rot(cc,i);
ll v;
enc(cc,v);
if(!d.count(v)) {
d[v]=d[u]+;
q.push(v);
}
}
}
} void prans() {
ll u;
for(u=ss; d[u];) {
dec(c,u);
for(int i=; i<; ++i) {
memcpy(cc,c,sizeof c);
rot(cc,i);
ll v;
enc(cc,v);
if(d.count(v)&&d[v]==d[u]-) {
printf("%c",i+'A');
u=v;
}
}
}
dec(c,u);
printf("\n%d\n",c[md[]]+);
} int input() {
for(int i=; i<; ++i)if(scanf("%d",&s[i])!=)return ;
return ;
} int main() {
while(input()) {
for(int i=; i<; ++i)s[i]--;
enc(s,ss);
bfs1();
bfs2();
prans();
}
return ;
}

这个版本TLE的原因是,如果直接bfs的话,总状态数为$C(24,8)*C(16,8)=9465511770$,显然太大了,即使加了剪枝状态数依然很大,妥妥地TLE。

但如果预先假定一个数是正确答案,那么剩下的两个数就没有区别了,所以状态可以用0和1来表示,这样总状态数就缩小到了$C(24,8)=735471$,在可接受范围内了。把原状态分解成三个子状态跑一遍bfs即可得到正确结果。但是如果对每个输入都跑一遍bfs的话依然会TLE,而我们发现对于每组输入,所有的状态表示的含义都是一样的,因此可以预先对末状态跑一遍bfs,记录下所有状态到末状态的最短距离,这样每接受一组输入都可以直接通过bfs树得到路径。

bfs的版本:(状态的保存用了哈希表,用stl中的map应该也可以)

 #include<bits/stdc++.h>

 using namespace std;
typedef long long ll;
const int N=+,inf=0x3f3f3f3f;
const int b[][]= {
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
};
int t[]= {,,,,,,,,,,,,,,,,,,,,,,};
void rot(int* c,int x) {
const int* bb=b[x];
for(int i=; i<; ++i)swap(c[bb[i]],c[bb[i+]]);
}
void enc(int* c,int& x) {
x=;
for(int i=; i>=; --i)x=x<<|c[i];
}
void dec(int* c,int x) {
for(int i=; i<; ++i)c[i]=;
for(int i=; i<; ++i)c[i]=x&,x>>=;
}
struct Hashmap {
static const int N=1e6+;
static const int mod=1e6+;
int hd[mod],nxt[N],tot,key[N],val[N];
int H(int x) {return (x+)%mod;}
void clear() {tot=; memset(hd,-,sizeof hd);}
int count(int x) {
for(int u=hd[H(x)]; ~u; u=nxt[u])if(key[u]==x)return ;
return ;
}
int& operator[](int x) {
int h=H(x);
for(int u=hd[h]; ~u; u=nxt[u])if(key[u]==x)return val[u];
nxt[tot]=hd[h],key[tot]=x,val[tot]=,hd[h]=tot;
return val[tot++];
}
} d; int c[N],cc[N],s[N],ss,tt,mi,ans;
string str1,str2; void bfs() {
d.clear();
queue<int> q;
q.push(tt),d[tt]=;
while(!q.empty()) {
int u=q.front();
q.pop();
dec(c,u);
for(int i=; i<; ++i) {
memcpy(cc,c,sizeof c);
rot(cc,i);
int v;
enc(cc,v);
if(!d.count(v)) {
d[v]=d[u]+;
q.push(v);
}
}
}
} int input() {
for(int i=; i<; ++i)if(scanf("%d",&s[i])!=)return ;
return ;
} int main() {
enc(t,tt);
bfs();
while(input()) {
mi=inf;
for(int i=; i<=; ++i) {
for(int j=; j<; ++j)c[j]=s[j]==i?:;
int u;
enc(c,u);
mi=min(mi,d[u]);
}
str1="Z";
for(int i=; i<=; ++i) {
for(int j=; j<; ++j)c[j]=s[j]==i?:;
int u;
enc(c,u);
if(d[u]==mi) {
str2.clear();
while(u!=tt) {
dec(c,u);
for(int j=; j<; ++j) {
memcpy(cc,c,sizeof c);
rot(cc,j);
int v;
enc(cc,v);
if(d.count(v)&&d[v]==d[u]-) {
str2.push_back(j+'A');
u=v;
break;
}
}
}
if(str2<str1)str1=str2,ans=i;
}
}
if(mi==)printf("No moves needed\n");
else printf("%s\n",str1.c_str());
printf("%d\n",ans);
}
return ;
}

另外一种方法是IDA*。看了看网上的题解,基本都是IDA*的做法。IDA*还是很有参考价值的。

设g(n)为从初始状态到当前状态n所需步数,h(n)为当前状态n到目标状态至少所需步数,则g(n)+h(n)>maxdep时剪枝。显然h(n)可以用中心格点中1,2,3中的最大个数与8的差来表示,这样就不用保存状态,直接搜就行了。IDA*特别适用于这种bfs树的宽度较大而深度较小的搜索问题。(可以用bfs跑一遍试试,会发现在用01状态表示法的情况下,最坏情况下达到目标状态也仅需14步。)

 #include<bits/stdc++.h>

 using namespace std;
typedef long long ll;
const int N=+,inf=0x3f3f3f3f;
const int b[][]= {
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
{,,,,,,},
};
const int md[]= {,,,,,,,};
void rot(int* c,int x,int f) {
const int* bb=b[x];
if(f==)for(int i=; i<; ++i)swap(c[bb[i]],c[bb[i+]]);
else for(int i=; i>=; --i)swap(c[bb[i]],c[bb[i+]]);
}
int count(int* c) {
int cnt[]= {};
for(int i=; i<; ++i)cnt[c[md[i]]-]++;
return max(cnt[],max(cnt[],cnt[]));
}
int s[N],ans;
char str[N]; bool dfs(int dep,int mxd) {
int cnt=count(s);
if(cnt==) {ans=s[md[]]; str[dep]='\0'; return ;}
if(-cnt>mxd-dep)return ;
for(int i=; i<; ++i) {
rot(s,i,);
if(dfs(dep+,mxd)) {str[dep]=i+'A'; return ;}
rot(s,i,-);
}
return ;
} void IDA() {
for(int mxd=;; ++mxd)if(dfs(,mxd))break;
if(str[])puts(str);
else puts("No moves needed");
printf("%d\n",ans);
} int input() {
for(int i=; i<; ++i)if(scanf("%d",&s[i])!=)return ;
return ;
} int main() {
while(input())IDA();
return ;
}

怎么样,代码是不是比bfs的版本简洁多了?而且速度出乎意料地比bfs还要快很多。

感觉IDA*就像暴搜+剪枝,bfs就像dp,具体该用哪个还得靠自己斟酌斟酌~~

UVA - 1343 The Rotation Game (BFS/IDA*)的更多相关文章

  1. UVa 1343 The Rotation Game (状态空间搜索 && IDA*)

    题意:有个#字型的棋盘,2行2列,一共24个格. 如图:每个格子是1或2或3,一共8个1,8个2,8个3. 有A~H一共8种合法操作,比如A代表把A这一列向上移动一个,最上面的格会补到最下面. 求:使 ...

  2. 【UVa】1343 The Rotation Game(IDA*)

    题目 题目     分析 lrj代码.... 还有is_final是保留字,害的我CE了好几发.     代码 #include <cstdio> #include <algorit ...

  3. UVA 1343 The Rotation Game

    题意: 给出图,往A-H方向旋转,使中间8个格子数字相同.要求旋转次数最少,操作序列字典序尽量小. 分析: 用一维数组存24个方格.二维数组代表每个方向对应的7个方格.IDA*剪枝是当8-8个方格中重 ...

  4. UVa 1343 旋转游戏(dfs+IDA*)

    https://vjudge.net/problem/UVA-1343 题意:如图所示,一共有8个1,8个2和8个3,如何以最少的移动来使得中间8个格子都为同一个数. 思路:状态空间搜索问题. 用ID ...

  5. UVA 1343 - The Rotation Game-[IDA*迭代加深搜索]

    解题思路: 这是紫书上的一道题,一开始笔者按照书上的思路采用状态空间搜索,想了很多办法优化可是仍然超时,时间消耗大的原因是主要是: 1)状态转移代价很大,一次需要向八个方向寻找: 2)哈希表更新频繁: ...

  6. UVA 816 -- Abbott's Revenge(BFS求最短路)

     UVA 816 -- Abbott's Revenge(BFS求最短路) 有一个 9 * 9 的交叉点的迷宫. 输入起点, 离开起点时的朝向和终点, 求最短路(多解时任意一个输出即可).进入一个交叉 ...

  7. 【例 7-12 UVA - 1343】The Rotation Game

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 迭代加深搜索. 每次抽动操作最多只会让中间那一块的区域离目标的"距离"减少1. 以这个作为剪枝. 枚举最大深度. ...

  8. UVa 11624,两次BFS

    题目链接:http://vjudge.net/contest/132239#problem/A 题目链接:https://uva.onlinejudge.org/external/116/11624. ...

  9. uva 11234 Expressions 表达式 建树+BFS层次遍历

    题目给出一个后缀表达式,让你求从下往上的层次遍历. 思路:结构体建树,然后用数组进行BFS进行层次遍历,最后把数组倒着输出就行了. uva过了,poj老是超时,郁闷. 代码: #include < ...

随机推荐

  1. Python基础(13)_python模块之re模块(正则表达式)

    8.re模块:正则表达式 就其本质而言,正则表达式(或 RE)是一种小型的.高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现.正则表达式模式被编译成一系列的字节 ...

  2. Protobuf支持 pointf

    Protobuf支持 pointf序列化 加入:ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typeof(System.Drawing.PointF), fa ...

  3. LeetCode:矩阵置零【73】

    LeetCode:矩阵置零[73] 题目描述 给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0.请使用原地算法. 示例 1: 输入: [   [1,1,1],   ...

  4. Loadrunder脚本篇——Run-time Settings之Miscellaneous

    作用说明 提供混杂设置,如错误处理,多线程,自动化事务设置等 注意:仅对指定协议有效   Error Handling Continue on Error 开启后,在VuGen中,如脚本中某个函数出错 ...

  5. MySQL密码的恢复方法

    MySQL密码的恢复方法之一 1.首先确认服务器出于安全的状态,也就是没有人能够任意地连接MySQL数据库. 因为在重新设置MySQL的root密码的期间,MySQL数据库完全出于没有密码保护的 状态 ...

  6. PHPExcel常用属性使用

    PHPExcel常用属性使用  前景: 需先实例化几个变量: $this->objExcel = new PHPExcel(); //实例化一个PHPExcel变量 $this->objE ...

  7. 计算机网络概述 传输层 TCP可靠传输的实现

    TCP可靠传输的实现 TCP的可靠性表现在:它向应用层提供的数据是 无差错的.有序的.无丢失的,简单的说就是:TCP最终递交给应用层的数据和发送者发送的数据是一模一样的. TCP采用了流量控制.拥塞控 ...

  8. Ubuntu16.04下编译android6.0源码

    http://blog.csdn.net/cnliwy/article/details/52189349 作为一名合格的android开发人员,怎么能不会编译android源码呢!一定要来一次说编译就 ...

  9. linux下安装eclipse并使用xstart远程使用(centos7)

    1 eclipse安装 1)到官网下载eclipse的linux版 http://www.eclipse.org/downloads/packages/eclipse-ide-java-ee-deve ...

  10. Django中间件的总结

    一.中间件 --中间件是一个轻量级.底层的插件系统,可以加入Django的请求和响应过程,修改Django的输入和输出 --每一个中间件组件是一个独立的Python类,可以定义下面方法中的一个和多个 ...