POJ1077.Eight
题目链接
题意:八数码问题,具体看题;
题目类型:广搜经典:(一个bug找了两天。。)
题解2;看了大神题解知道了康托展开,X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!,可以很大程度上减小数据的大小,就不用map了;
Code:
题解*:大佬题解上讲的,看了只觉得好强,我们要的最后一个状态是{1, 2, 3, 4, 5, 6, 7, 8, x}, 这个排列的逆序数是0,
是偶数。而对于任意一个排列,显然,因为x不表示数字,那么也就是说把x向左右两个方向移动并不会改变整个序列
的逆序数,那么有结论;如果初始序列的逆序数是一个奇数,则必然这个序列无解(必要,不充分)
题解**:还有其他做法双向搜索,A*,IDA*(都不会。。。以后补上Orz)
题目链接
题意:八数码问题,具体看题;
题目类型:广搜经典:(一个bug找了两天。。)
题解0:起初想通过map<struct dd,struct dd >瞎搞,发现map不能用结构体做KEY值,搜到了重载运算,用结构体做KEY值的方法,不懂。。还是记一下;
Code:
struct eight{//这招记下来...少用
//int a[9];数组也不能做键值
int sum;
int dirc;
int pz;
bool operator<(const struct eight & other) const {
if (this->sum < other.sum) return true;
if (this->dirc < other.dirc) return true;
if (this->pz < other.pz) return true;
return false;
}
}M,goal;
题解1:通过一个函数将整个的状态映射到一个数上,将x所在位置用0表示,初步思路f(a)=a1*10+a2*100+a3*1000+a4*10000+a5*100000+....a9*1000000000;
然后广搜时,注意各种细节。。。这搜索太mmp了,以后要多刷这种题磨练意志;然而还是没过mle+tle;
Code:
//改进做法tle
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<time.h>
#define ll long long
using namespace std;
int a[9];
char res[1005];
map<int,char> c;
map<int,int> pre;
map<int,int> vist;
int getidex(int*a)
{
int uu=0;
for(int i=0; i<9; i++)
{
uu=uu*10+a[i];
}
return uu;
}
void getnode(int m)
{
int k=8;
memset(a,0,sizeof(a));
while(k>=0)
{
a[k]=m%10;
m/=10;
k--;
}
}
void debug()
{
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
printf("%d ",a[i*3+j]);
}
printf("\n");
}
system("pause");
}
void BFS(int n)
{
int k,t=n,p;
queue<int> q;
q.push(n);
// for(int i=0;i<9;i++)
// printf("%d ",a[i]);
// printf("\n");
k=getidex(a);
pre[k]=-1;
vist[k]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
getnode(u);
for(int j=0; j<9; j++)
if(a[j]==0)
{
p=j;
break;
}
//printf("zp-->|%d\n",p);
if(p<6)
{
swap(a[p],a[p+3]);
k=getidex(a);
//printf("d-->k//%d\n",k);
//debug();
if(!vist[k])
{
vist[k]=1;
pre[k]=u;
c[k]='d';
q.push(k);
}
swap(a[p],a[p+3]);
}
if(p%3)
{
swap(a[p-1],a[p]);
k=getidex(a);
//printf("l-->k//%d\n",k);
//debug();
if(!vist[k])
{
vist[k]=1;
pre[k]=u;
c[k]='l';
q.push(k);
}
swap(a[p],a[p-1]);
}
if(p%3<2)
{
swap(a[p+1],a[p]);
k=getidex(a);
//printf("r-->k//%d\n",k);
//debug();
if(!vist[k])
{
vist[k]=1;
pre[k]=u;
c[k]='r';
q.push(k);
}
swap(a[p],a[p+1]);
}
if(p>2)
{
swap(a[p-3],a[p]);
k=getidex(a);
//printf("u-->k//%d\n",k);
//debug();
if(!vist[k])
{
vist[k]=1;
pre[k]=u;
c[k]='u';
q.push(k);
}
swap(a[p],a[p-3]);
}
if(vist[123456780])break;
}
if(vist[123456780])
{
int u=123456780,x=u,cnt=0;
while(x!=-1)
{
//printf("%d %c\n",cnt,c[x]);
res[cnt++]=c[x];
x=pre[x];
}
for(int i=cnt-2; i>=0; i--)
{
printf("%c",res[i]);
}
printf("\n");
}
else
printf("unsolvable\n");
}
int main()
{
int uu=0;
for(int i=0; i<9; i++)
{
char c;
scanf(" %c",&c);
if(c=='x'||c=='X')
{
a[i]=0;
uu=uu*10;
}
else
{
a[i]=c-'0';
uu=uu*10+a[i];
}
}
// for(int i=0;i<9;i++)printf(" %d",a[i]);
// printf("\n");
//printf("uu//%d\n",uu);
// time_t ts,te;
//ts=time(NULL);
BFS(uu);
//te=time(NULL);
//printf("%ld\n",te-ts);
//system("pause");
return 0;
}
题解2;看了大神题解知道了康托展开,X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!,可以很大程度上减小数据的大小,就不用map了;
Code:
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<time.h>
#define ll long long
using namespace std;
int vis[362880];
int fa[362880];
char step[362880];
int fac[]={1,1,2,6,24, 120, 720, 5040, 40320, 362880};
int dir[4][2] = {{-1, 0}, {1,0}, {0, -1}, {0, 1}}; //u, d, l, r
struct node
{
char s[9];
int space;
};
int Hash(const char* str)
{
int n=9,num=0,temp;
for(int i=0;i<n-1;i++)
{
temp=0;
for(int j=i+1;j<n;j++)
{
if(str[i]>str[j])temp++;
}
num+=temp*fac[str[i]-1];
}
return num;
}
void get_node(int num, node &temp)
{
int n=9;
int a[9];
for(int i=2;i<=n;i++)
{
a[i-1]=num%i;
num/=i;
temp.s[i-1]=0;
}
temp.s[0]=0;
int rn,i;
for(int k=n;k>=2;k--)
{
rn=0;
for(i=n-1;i>=0;i--)
{
if(temp.s[i]!=0)
continue;
if(rn==a[k-1])
break;
rn++;
}
temp.s[i]=k;
}
for(int i=0;i<n;i++)
{
if(temp.s[i]==0)
{
temp.s[i]=1;
break;
}
}
temp.space=n-a[n-1]-1;
}
void bfs(const node&begin)
{
memset(vis, 0, sizeof(vis));
int u = Hash(begin.s);
vis[u] = 1;
fa[u] = -1;//便于后续输出路径,保存了每一步的父节点
queue<int> q;
q.push(u);
node now,next;
while(!q.empty())
{
u = q.front();
q.pop();
get_node(u, now);
int k = now.space;
int x = k/3;
int y = k%3;
for(int i=0;i<4;i++)
{
int xx = x + dir[i][0];
int yy = y + dir[i][1];
if(xx>=0&&xx<=2&&yy>=0&&yy<=2)//yy写成y。。。RE好几年
{
next=now;
next.space=xx*3+yy;
swap(next.s[k],next.s[next.space]);
int v=Hash(next.s);
if(!vis[v])
{
vis[v]=1;
step[v]=i;
fa[v]=u;
if(v==0)//
{
return;
}
q.push(v);
}
}
}
}
}
void print()
{
int n, u;
char path[1000];
n = 1;
path[0] = step[0];
u = fa[0];
while(fa[u]!=-1)
{
path[n] = step[u];
++n;
u = fa[u];
}
for(int i=n-1; i>=0; --i)
{
if(path[i]==0)
cout << "u";
else if(path[i]==1)
cout << "d";
else if(path[i]==2)
cout << "l";
else
cout << "r";
}
}
int main()
{
char c;
node start;
for(int i=0;i<9;i++)
{
scanf(" %c",&c);
if(c=='x')
{
start.s[i]=9;
start.space=i;
}
else
{
start.s[i]=c-'0';
}
}
bfs(start);
if(vis[0]==1)
print();
else
cout << "unsolvable";
cout << endl;
return 0;
}
题解*:大佬题解上讲的,看了只觉得好强,我们要的最后一个状态是{1, 2, 3, 4, 5, 6, 7, 8, x}, 这个排列的逆序数是0,
是偶数。而对于任意一个排列,显然,因为x不表示数字,那么也就是说把x向左右两个方向移动并不会改变整个序列
的逆序数,那么有结论;如果初始序列的逆序数是一个奇数,则必然这个序列无解(必要,不充分)
题解**:还有其他做法双向搜索,A*,IDA*(都不会。。。以后补上Orz)