题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043

解题背景:

看到八数码问题,没有任何的想法,偶然在翻看以前做的题的时候发现解决过类似的一道题,不过那题是求最长的移动步骤,给你的时间有10s之多,用set和queue过了它,这题我套用当时的模板,具体解题点击这里,发现TLE了一辈子。后来直接敲了一遍lrj白书上关于八数码的代码,还是TLE,一天就这样过去了,决定看别人的思路,很快就找到Kuangbin神的代码,代码思路基本相似,只是哈希函数的差别,TLE的原因没其他,卡就卡你的哈希,搜索了多份代码,反复地提到了康托展开,百度之后决定搞懂其。

百度之后发现其只是给你介绍什么是康托展开,然后告诉你康托展开可以用代码实现,然后他就将其用代码展示予你看。||- _ -

康托展开可以看做是特殊的哈希函数,而且是不会产生冲突,固定位数的数在排列组合中得到的数按大小比较的话都有一个特定的位置,康托展开就是计算这个数在当中的排列位置,百度中你会看见:(只是想说清楚这段文字和实现代码的关系)

其举的第二个例子中,即判断1324在排列数中是第几大的数,有一个式子的模型是:X *  Y!,X表示当前数到的位数其后面的数有几个比其小,Y跟此时数到的位数有关,其值是这个排列数的总的位数减去当前数到的位数,即剩下来有几位,Y!的意义是剩下的位数进行排列组合,而X表示有几个数能替代当前数到的位数当前的数,比如说刚数到第一个数时,这里表示能替代1的数有几个(比1小的数),因为你的目的是看这个排列数是第几大的数。对于1324中的1来说为0个;这时只是处理了第一个位置的位数,接下来从左往后还有需要数到的位数,Y也依次减一。最终累加的数才能得出在排列数是第几大的数。实现的代码将这些阶乘都先计算出来。

用此为哈希函数个人感觉有点像位运算通过二进制得到独一无二的数,学习ing。

 #include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#define MAXN 1000003
#define SIZE 9
using namespace std; typedef int State[SIZE];
int dir[][] = {{, -}, {, }, {-, }, {, }};
string dirFlag[] = {"l", "r", "u", "d"};
State st[MAXN], goal = {,,,,,,,,};
string dist[MAXN];
int head[MAXN], next[MAXN]; int hash(State& s)
{
int v = ;
for(int i=; i<SIZE; ++i) v = v* + s[i];
return v%MAXN;
} int try_to_insert(int s)
{
int h = hash(st[s]);
int u = head[h];
while(u)
{
if(memcmp(st[u], st[s], sizeof(st[s])) == ) return ;
u = next[u];
}
next[s] = head[h];
head[h] = s;
return ;
} int Traverse()
{
memset(head, , sizeof(head));
int front = , rear = ;
dist[front] = "";
while(front < rear)
{
State& s = st[front];
if(memcmp(goal, s, sizeof(s)) == ) return front;
int z;
for(z=; z<SIZE; ++z) if(!s[z]) break;
int x = z/, y = z%;
for(int d=; d<; ++d)
{
int newx = x + dir[d][];
int newy = y + dir[d][];
int newz = newx * + newy;
if(newx >= && newx < && newy >= && newy < )
{
State& t = st[rear];
memcpy(&t, &s, sizeof(s));
t[newz] = s[z];
t[z] = s[newz];
dist[rear].assign(dist[front]);
dist[rear].append(dirFlag[d]);
if(try_to_insert(rear)) rear++;
}
}
front++;
}
return ;
} int main()
{
freopen("F:\\test\\input.txt", "r", stdin);
char ch;
while(cin>>ch)
{
if(ch == 'x') st[][] = ;
else st[][] = ch - '';
for(int i=; i<SIZE; ++i)
{
cin>>ch;
if(ch == 'x') ch = '';
st[][i] = ch - '';
}
int ans = Traverse();
if(ans > ) cout<<dist[ans]<<endl;
else cout<<"unsolvable"<<endl;
}
return ;
}

TLE 代码

WA的原因:

如果按照普通的想法来做,一般就像我刚看到题所想到,每一个case都去遍历一遍,每一次都去判段能不能走通这条路,因为是从不定的情况到确定的情况,如果是不可能走通的情况,那么每个case消耗的时间都是最大的,所以TLE是不可避免了。相反,从反过来的情况去判断,一次就能记录下来所有不能达到的情况和记录可以到达情况的步骤。傻傻的就这样想不通,这样我想起了很久之前做的一道题,每个Case都去筛素数.....隔了那么长的时间,思维却丝毫没有改变,嗒嗒

 #include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<queue>
#define MAXN 362888
#define SIZE 9
using namespace std; typedef int State[SIZE];
int dir[][] = {{, -}, {, }, {-, }, {, }};
char dirFlag[] = "rldu"; typedef struct Status{
State value;
}Status; queue<Status>queuing; string st[MAXN];
bool visit[MAXN]; int factory[] = {, , , , , , , , , };
const int start = ;
int aim = ;
int input[SIZE]; int try_to_insert(int s[])
{
int sum = ;
for(int i=; i<SIZE; ++i)
{
int cnt = ;
for(int j=i+; j<SIZE; ++j)
if(s[i]>s[j]) ++cnt;
sum += cnt*factory[SIZE-i-];
}
return sum;
} void Traverse()
{
memset(visit, false, sizeof(visit));
Status init;
for(int i=; i<SIZE-; ++i) init.value[i] = i+;
init.value[SIZE-] = ;
visit[start] = true;
st[start] = "";
queuing.push(init);
while(!queuing.empty())
{
Status ss = queuing.front();
State& s = ss.value;
queuing.pop();
int z;
for(z=; z<SIZE; ++z) if(!s[z]) break;
int x = z/, y = z%;
for(int d=; d<; ++d)
{
int newx = x + dir[d][];
int newy = y + dir[d][];
int newz = newx * + newy;
if(newx >= && newx < && newy >= && newy < )
{
Status tt;
State& t = tt.value;
memcpy(t, s, sizeof(s));
t[newz] = s[z];
t[z] = s[newz];
int elem = try_to_insert(s);
int adr = try_to_insert(t);
if(!visit[adr])
{
visit[adr] = true;
st[adr] = dirFlag[d] + st[elem];
queuing.push(tt);
}
}
}
}
return;;
} int main()
{
// freopen("F:\\test\\input.txt", "r", stdin);
char ch;
Traverse();
while(cin>>ch)
{
if(ch == 'x') input[] = ;
else input[] = ch - '';
for(int i=; i<SIZE; ++i)
{
cin>>ch;
if(ch == 'x') ch = '';
input[i] = ch - '';
}
aim = try_to_insert(input);
if(visit[aim]) cout<<st[aim]<<endl;
else cout<<"unsolvable"<<endl;
}
return ;
}

HDU ACM Eight的更多相关文章

  1. hdu acm 1028 数字拆分Ignatius and the Princess III

    Ignatius and the Princess III Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K ...

  2. hdu acm 1166 敌兵布阵 (线段树)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  3. hdu acm 2082 找单词

    找单词 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  4. HDU ACM 1325 / POJ 1308 Is It A Tree?

    Is It A Tree? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  5. HDU ACM 1134 Game of Connections / 1130 How Many Trees?(卡特兰数)

    [题目链接]http://acm.hdu.edu.cn/showproblem.php?pid=1134 [解题背景]这题不会做,自己推公式推了一段时间,将n=3和n=4的情况列出来了,只发现第n项与 ...

  6. HDU ACM 题目分类

    模拟题, 枚举1002 1004 1013 1015 1017 1020 1022 1029 1031 1033 1034 1035 1036 1037 1039 1042 1047 1048 104 ...

  7. HDU ACM 1690 Bus System (SPFA)

    Bus System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  8. HDU ACM 1224 Free DIY Tour (SPFA)

    Free DIY Tour Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  9. HDU ACM 1869 六度分离(Floyd)

    六度分离 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

随机推荐

  1. ubuntu 映射网络驱动器到本地

    公司办公都是在ubuntu服务器上,所以每每拷贝修改文件都要ftp之类的,实在不方便. 索性将服务器挂载到自己本地目录下. 服务器端参考其他samba安装和配置.这里只是说下本地自动挂载方法. 一.首 ...

  2. 计算机技能get(windows系统)

    1.快速打开程序,比如计算器,注册表,先按win键(不用再按win+r啦),输入程序名字,如calc,regedit等,直接打开. 2.自动左右分屏,win+上下左右方向键,win+↑ 最大化,win ...

  3. (贪心5.2.1)UVA 10026 Shoemaker's Problem(利用数据有序化来进行贪心选择)

    /* * UVA_10026.cpp * * Created on: 2013年10月10日 * Author: Administrator */ #include <iostream> ...

  4. POJ 2065 SETI(高斯消元)

    题目链接:http://poj.org/problem?id=2065 题意:给出一个字符串S[1,n],字母a-z代表1到26,*代表0.我们用数组C[i]表示S[i]经过该变换得到的数字.给出一个 ...

  5. trackr: An AngularJS app with a Java 8 backend – Part IV 实践篇

    REST API对于前后端或后端与后端之间通讯是一个好的接口,而单页应用Single Page Applications (SPA)非常流行. 我们依然以trackr为案例,这是一个跟踪工作时间 请假 ...

  6. trackr: An AngularJS app with a Java 8 backend – Part II

    该系列文章来自techdev The Frontend 在本系列的第一部分我们已经描述RESTful端建立在Java 8和Spring.这一部分将介绍我们的第一个用 AngularJS建造的客户端应用 ...

  7. bzoj3926

    题目的意思是叶子不超过20个……听说当初zjoi不少人被坑 分别对每个叶子以它为根dfs出20个dfs树,这样整个树的任何一个子串,都是某个dfs树上一个点到它的一个子孙的路径 每个dfs树,根到叶子 ...

  8. iOS开发:记录开发中遇到的编译或运行异常以及解决方案

    1.部署到真机异常 dyld`dyld_fatal_error: ->  0x120015088 <+0>: brk    #0x3 dyld: Library not loaded ...

  9. UVa 10791 (唯一分解) Minimum Sum LCM

    题意: 输入n,求至少两个正整数,使得这些数的最小公倍数为n且和最小. 分析: 设n的分解式为,很显然单独作为一项,和最小. 这里有两个小技巧: 从2开始不断的除n,直到不能整除为止.这样就省去了素数 ...

  10. parentNode(返回指定节点的父节点。)

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...