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 < ...
随机推荐
- [不常用] - CSRF(跨站点请求伪造)
CSRF,Cross Site Request Forgery,即跨站点请求伪造. 这种攻击是指,在用户正常登录系统以后,攻击者诱使用户访问一些非法链接,以执行一些非法操作. 比如:如果删除用户操 ...
- input propertychange(1)
input type=“text” 通过js改变输入框的value值是不会出发input propertychange事件
- 每天一个Linux命令(38)top命令
top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器. (1)用法: 用法: top [参数] top是 ...
- Vosio秘钥
C2FG9-N6J68-H8BTJ-BW3QX-RM3B32NYF6-QG2CY-9F8XC-GWMBW-29VV8FJ2N7-W8TXC-JB8KB-DCQ7Q-7T7V3VXX6C-DN3HQ-3 ...
- JSP笔记03——环境搭建(转)
不完全翻译,结合谷歌,一定主观性,还可能有误,原始内容地址:https://www.tutorialspoint.com/jsp/jsp_environment_setup.htm [注释]这篇貌似有 ...
- console、JSON兼容问题
console在ie8上面竟然有兼容问题,JSON.stringify()在ie10下竟然会报错,再页面上引用一个json2.js能解决此问题.
- matplotlib模块之子图画法
一般化的子图布局 首先要创建各个子图的坐标轴,传入一个四元列表参数:[x,y,width,height],用来表示这个子图坐标轴原点的x坐标.y坐标,以及宽和高.值得注意的是,这四个值的取值范围都是[ ...
- .NET应用程序默认使用管理员身份打开
1.在源码的Properties目录中找到 app.manifest(如果没有进入第二步,有跳过第二步) 2.如果没有app.manifest文件可以打开项目属性,找到安全性项,勾上启用 ClickO ...
- Centos7.4 Nginx反向代理+负载均衡配置
Ningx是一款高性能的HTTP和反向代理服务器,配置起来也比较简单. 测试环境: 172.16.65.190 Nginx-反向代理 172.16.65.191 Ningx-Web 172.16.65 ...
- freemarker日志实现过程分析
freemarker有自己的log类,这是一个抽象类,具体的日志打印委托给classpath里面合适的日志jar包来执行,寻找合适日志jar的查找顺序是:Apache Log4J, Apache Av ...