题目链接: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. 10位顶级PHP大师的开发原则

    在Web开发世界里,PHP是最流行的语言之一,从PHP里,你能够很容易的找到你所需的脚本,遗憾的是,很少人会去用“最佳做法”去写一个PHP程序.这里,我们向大家介绍PHP的10种最佳实践,当然,每一种 ...

  2. js 返回的数据类型 5类

    对变量或值调用 typeof 运算符将返回下列值之一: undefined - 如果变量是 Undefined 类型的 boolean - 如果变量是 Boolean 类型的 number - 如果变 ...

  3. Android安卓开发中图片缩放讲解

    安卓开发中应用到图片的处理时候,我们通常会怎么缩放操作呢,来看下面的两种做法: 方法1:按固定比例进行缩放 在开发一些软件,如新闻客户端,很多时候要显示图片的缩略图,由于手机屏幕限制,一般情况下,我们 ...

  4. HDU 4948

    题目大义: 给一张图,任意两点间有单向边,找出一种方案,使得每个新入队的点与队中的点距离<=2. 题解: 贪心,从最后入队点开始反向插入,每次找出最大入度的点入队. 只需证明最大入度点A与所有未 ...

  5. ZJOI2006物流运输

    唉,没想出来…… 注意到预处理的作用.还有CLJ大牛说的话:这么小的数据,想干什么都可以. SPFA预处理+DP 够经典 var f:..,..]of longint; a:..,..]of bool ...

  6. 使用讯飞SDK,实现文字在线合成语音

    private SpeechSynthesizer mTts; private int isSpeaking = 0; mTts= SpeechSynthesizer.createSynthesize ...

  7. 给你一个承诺 - 玩转 AngularJS 的 Promise(转)

    在谈论Promise之前我们要了解一下一些额外的知识:我们知道JavaScript语言的执行环境是“单线程”,所谓单线程,就是一次只能够执行一个任务,如果有多个任务的话就要排队,前面一个任务完成后才可 ...

  8. sublime3 常用功能总结

    介绍几个常见的功能: l 自动完成:自动完成的快捷键是Tab和Enter,如果在html文件中,输入cl按下tab或Enter,即可自动补全为class=””:加上zencoding后,更是如虎添翼, ...

  9. Java [Leetcode 219]Contains Duplicate II

    题目描述: Given an array of integers and an integer k, find out whether there are two distinct indices i ...

  10. Oracle中job的使用详解

    我们在项目开发中,常常会有一些复杂的业务逻辑.使用oracle的存储过程,可以大大减少java程序代码的编写工作量,而且存储过程执行在数据库上,这样可以利用oracle的良好性能支持,极大地提高程序执 ...