UVA - 1343 The Rotation Game (BFS/IDA*)

时间:2021-12-03 23:57:09

题目链接

紫书例题。

首先附上我第一次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 &lpar;状态空间搜索 &amp&semi;&amp&semi; IDA&ast;&rpar;

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

  2. 【UVa】1343 The Rotation Game(IDA&ast;)

    题目 题目     分析 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&plus;IDA&ast;)

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

  5. UVA 1343 - The Rotation Game-&lbrack;IDA&ast;迭代加深搜索&rsqb;

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

  6. UVA 816 -- Abbott&&num;39&semi;s Revenge&lpar;BFS求最短路&rpar;

     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 表达式 建树&plus;BFS层次遍历

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

随机推荐

  1. adb install INSTALL&lowbar;FAILED&lowbar;ALREADY&lowbar;EXISTS

    安装时候碰到的一个问题:已经签名的包,重新通过adb install 会提示安装错误.提示:Failure [INSTALL_FAILED_ALREADY_EXISTS] 为啥eclipse自己就可以 ...

  2. VS2010添加类失败问题&comma;弹出错误框&comma;提示 CodeModel操作失败,无法访问标记数据库

    我在使用VS2010添加类的时候,会弹出一个错误框,提示 CodeModel操作失败,可以无法访问标记数据库 英文版是 CodeModel operation failed,Possibly cann ...

  3. ScrollView与ListView的冲突

    众所周知ListView与ScrollView都具有滚动能力,对于这样的View控件,当ScrollView与ListView相互嵌套会成为一种问题: 问题一:ScrollView与ListView嵌 ...

  4. Solaris 10下Qt编译Oracle 10g驱动

    上回书讲到<Oracle 10g在Solaris 10中安装详解>,现在开始用Qt来编译下Oracle 10g驱动吧!这样就可以通过Qt程序联入Oracle数据库了! Oracle的环境变 ...

  5. opengl截图

    int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) { UINT num = ; // number of image encoders U ...

  6. Android开发之万能适配器

    ListView.GridView等等非常多的东西都需要适配器.而如果开发一个app每一个listview都有写一个Adapter的话,那还怎么愉快的玩游戏.. 什么是ViewHolider以及的用法 ...

  7. 企业级Docker私有仓库之Harbor部署&lpar;http&rpar;

    部署环境 Centos7.3 x64 docker-ce-17.06.0 docker-compose-1.15.0 Python-2.7.5(系统默认) Docker及Docker-compose安 ...

  8. Java 学习体系结构

    一. JavaWEB 阶段课程体系结构 java se基础学习 二 .JavaWEB 阶段课程体系结构 第一阶段:前端开发阶段 1 HTML   2 CSS JS    3JS    4 JQuery ...

  9. POJ 1321 棋盘问题(搜索的方式)

    Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子 ...

  10. Django学习笔记之数据库-模型的操作

    模型的操作 在ORM框架中,所有模型相关的操作,比如添加/删除等.其实都是映射到数据库中一条数据的操作.因此模型操作也就是数据库表中数据的操作. 添加模型 添加模型到数据库中.首先需要创建一个模型.创 ...