UVA - 1343 The Rotation Game (BFS/IDA*)
紫书例题。
首先附上我第一次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*)的更多相关文章
- UVa 1343 The Rotation Game (状态空间搜索 && IDA*)
题意:有个#字型的棋盘,2行2列,一共24个格. 如图:每个格子是1或2或3,一共8个1,8个2,8个3. 有A~H一共8种合法操作,比如A代表把A这一列向上移动一个,最上面的格会补到最下面. 求:使 ...
- 【UVa】1343 The Rotation Game(IDA*)
题目 题目 分析 lrj代码.... 还有is_final是保留字,害的我CE了好几发. 代码 #include <cstdio> #include <algorit ...
- UVA 1343 The Rotation Game
题意: 给出图,往A-H方向旋转,使中间8个格子数字相同.要求旋转次数最少,操作序列字典序尽量小. 分析: 用一维数组存24个方格.二维数组代表每个方向对应的7个方格.IDA*剪枝是当8-8个方格中重 ...
- UVa 1343 旋转游戏(dfs+IDA*)
https://vjudge.net/problem/UVA-1343 题意:如图所示,一共有8个1,8个2和8个3,如何以最少的移动来使得中间8个格子都为同一个数. 思路:状态空间搜索问题. 用ID ...
- UVA 1343 - The Rotation Game-[IDA*迭代加深搜索]
解题思路: 这是紫书上的一道题,一开始笔者按照书上的思路采用状态空间搜索,想了很多办法优化可是仍然超时,时间消耗大的原因是主要是: 1)状态转移代价很大,一次需要向八个方向寻找: 2)哈希表更新频繁: ...
- UVA 816 -- Abbott's Revenge(BFS求最短路)
UVA 816 -- Abbott's Revenge(BFS求最短路) 有一个 9 * 9 的交叉点的迷宫. 输入起点, 离开起点时的朝向和终点, 求最短路(多解时任意一个输出即可).进入一个交叉 ...
- 【例 7-12 UVA - 1343】The Rotation Game
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 迭代加深搜索. 每次抽动操作最多只会让中间那一块的区域离目标的"距离"减少1. 以这个作为剪枝. 枚举最大深度. ...
- UVa 11624,两次BFS
题目链接:http://vjudge.net/contest/132239#problem/A 题目链接:https://uva.onlinejudge.org/external/116/11624. ...
- uva 11234 Expressions 表达式 建树+BFS层次遍历
题目给出一个后缀表达式,让你求从下往上的层次遍历. 思路:结构体建树,然后用数组进行BFS进行层次遍历,最后把数组倒着输出就行了. uva过了,poj老是超时,郁闷. 代码: #include < ...
随机推荐
- CSS中input输入框点击时去掉外边框方法【outline:medium;】----CSS学习
CSS 中添加 outline:medium; JS 控制焦点: $("#CUSTOM_PHONE").focus(function(event){ // this.attr(&q ...
- python3 multiprocessing 模块
多进程 Multiprocessing 模块 multiprocessing 模块官方说明文档 Process 类 Process 类用来描述一个进程对象.创建子进程的时候,只需要传入一个执行函数和函 ...
- Mac 环境 下使用Charles 抓包Http/Https请求
实现目标 在Mac 上 对 iOS 真机 和 模拟器 进行 Http/Https抓包 使用工具 Mac 上 Charles 4.2 安装 参考链接 1. 和 链接 2. 抓包 http 请求 (1 ...
- 所有文本的 attributes 枚举,NSAttributedString
// Predefined character attributes for text. If the key is not in the dictionary, then use the defau ...
- Linux静默安装weblogic
本实验安装weblogic10系列版本 #创建weblogic用户组. [root@admin /]# groupadd weblogic[root@admin /]# useradd -g webl ...
- 摊铺机基本参数介绍(鼎盛天工WTD9501A)
柴油水冷发动机,马力强劲,功率储备系数大,低噪音.低污染,经济性好,低温起动性能好.微电子控制,分别实现刮板输送.螺旋供料左右独立驱动,可实现自动供料,保持熨平板前物料均匀,调平系统响应速度快,调平精 ...
- 20145240《Java程序设计》第二周学习总结
20145240 <Java程序设计>第二周学习总结 教材学习内容总结 本周主要学习了第三章的内容,讲的是Java的一些基础语法,包括两大系统类型.变量.运算符的基本使用.类型转换及几个基 ...
- Java 访问修饰符总结
Java中的访问修饰符 Java面向对象的基本思想之一是封装细节并且公开接口. Java语言采用访问控制修饰符来封装类及类的方法和属性的访问权限,从而向使用者暴露接口.隐藏细节. Java访问控制分为 ...
- java instrumentation &JVMTI
Java Instrumentation (参考:http://www.ibm.com/developerworks/cn/java/j-lo-jse61/) 简介: 使用Instrumentatio ...
- apache基于端口的虚拟主机配置
主机ip: 192.168.7.51 Centos6.5 三个目录/usr/ftp/test/usr/ftp/dev/usr/ftp/demo 实现效果192.168.7.51:8052访问/usr/ ...