题目链接: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. 源代码Log

    MVC5源代码 https://github.com/aspnet/Mvc MVC4源代码 http://aspnetwebstack.codeplex.com/

  2. 安卓开发44:解决 INSTALL_FAILED_UID_CHANGED 等问题

    apk无法卸载,一般可以下面的方法试一下: 1. 删除/data/app/(filename) 文件夹下的apk包 2. 删除/system/app/(filename) 文件夹下的apk包 3. 将 ...

  3. [原创]Android中LocationManager的简单使用,获取当前位置

    Android中LocationManager的提供了一系列方法来地理位置相关的问题,包括查询上一个已知位置:注册/注销来自某个 LocationProvider的周期性的位置更新:以及注册/注销接近 ...

  4. vs2008 编译时候 自动关闭 问题解决方法

    最近又出现如此让人崩溃的问题.      vs2008在编译程序时候老是莫名其妙的自动退出.卸载重装以后问题仍然存在.      害我一度以为是vs2008的BUG,看网上说的更新BUG.      ...

  5. rsync不存在用户处理CPU消耗拒绝服务漏洞

    受影响产品: rsync 3.1.0 漏洞描述: CVE ID:CVE-2014-2855 rsync是一款文件同步管理软件. rsync处理不存在用户时存在安全漏洞,可消耗大量CPU资源,造成拒绝服 ...

  6. LeetCode Linked List Cycle 单链表环

    题意:给一个单链表,判断其是否出现环! 思路:搞两个指针,每次,一个走两步,另一个走一步.若有环,他们会相遇,若无环,走两步的指针必定会先遇到NULL. /** * Definition for si ...

  7. 【C#学习笔记】结构体使用

    using System; namespace ConsoleApplication { struct _st { public string name; public int age; } clas ...

  8. jrtplib跨网络通讯NAT穿透问题解决方法

    前几篇文章讲了使用jrtplib在Android和pc端进行通讯的方法 在实际项目中,手机端和pc端一般不会在同一个子网内,两者之间联络可能要走路由器之类的NAT(网络地址转换 Network Add ...

  9. java后台调用HttpURLConnection类模拟浏览器请求(一般用于接口调用)

    项目开发中难免遇到外部接口的调用,小生今天初次接触该类,跟着API方法走了一遍,如有不对的地方,还请哆哆指正,拜谢! 1 package com.cplatform.movie.back.test; ...

  10. Oracle RAC 客户端连接负载均衡(Load Balance)

    实现负载均衡(Load Balance)是Oracle RAC最重要的特性之一,主要是把负载平均分配到集群中的各个节点,以提高系统的整体吞吐能力.通常情况下有两种方式来实现负载均衡,一个是基于客户端连 ...