HDU 3567 Eight II 打表,康托展开,bfs,g++提交可过c++不可过 难度:3

http://acm.hdu.edu.cn/showproblem.php?pid=3567

相比Eight,似乎只是把目标状态由确定的改成不确定的,但是康托展开+曼哈顿为h值的A*和IDA*都不过,而且也不好控制字典序

换个角度想,虽然起始状态有很多,但是到底哪一位是1,哪一位是2不是最重要的,最重要的是和目标状态对应,所以可以把起始状态重新编码为"12345678"这种形式(先不考虑X),然后目标状态也对应过去,当考虑X的时候,我们可以认为起始状态只有9种,分别是‘X‘在各个位置的情况

利用bfs可以得到每种起始状态到对应的能够达到的所有目标状态的最短最小字典序转移方法,如果对每个状态都记录一个字符串肯定会MLE,所以只需要记录上一个状态到这一个状态转移所需要的操作即可

最后,对于每个目标状态,逆向推出状态转移序列,然后逆着输出即可

ps:一次处理3.6e6个状态,如果不用queue或者循环数组会MLE,如果不用g++提交而用c++提交会TLE

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cctype>
using namespace std;
const int base[9]={1,1,2,6,24,120,720,5040,40320};
const int dx[4]={1,0,0,-1};/转移方向,遵从字典序最小
const int dy[4]={0,-1,1,0};
const int maxh=362880;//每种开始状态所对应状态数
int des;//用于存储目标状态hash值,判断是否不需要转移
struct pnt{
        int maz[3][3];
        int ind;//标记起始状态的编号
        int x,y;
        int hashCode;
        pnt(){
                x=y=hashCode=-1;
        }
        pnt(char * str){
                for(int i=0;i<9;i++){
                        if(str[i]==‘X‘){
                                maz[i/3][i%3]=9;
                                x=i/3;
                                y=i%3;
                                ind=i;
                        }
                        else maz[i/3][i%3]=str[i]-‘0‘;
                }
        }
        int gethashcode(){
                int ans=0;
                for(int i=0;i<9;i++){
                        int cnt=0;
                        for(int j=0;j<i;j++){
                                if(maz[j/3][j%3]>maz[i/3][i%3]){
                                        cnt++;
                                }
                        }
                        ans+=base[i]*cnt;
                }
                return hashCode=ind*maxh+ans;
        }
};
bool in(int tx,int ty){
        return tx>=0&&tx<3&&ty>=0&&ty<3;
}

int vis[maxh*10+1];//用于存储某状态是否已被遍历,并存储由何种操作得到该状态
int pre[maxh*10+1];//用于存储某状态由何种状态转移得到
queue<pnt> que;
char ans[maxh*10+1];//存储答案的逆序
char buff[300];//存储输入
pnt s,e;
int ind[10],indlen;//用于重新编码
bool input(){
        if(scanf("%s",buff)!=1)return false;
        indlen=0;//重新编码
        for(int i=0;i<9;i++){
                if(buff[i]>=‘0‘&&buff[i]<=‘9‘){
                        s.maz[i/3][i%3]=++indlen;
                        ind[buff[i]-‘0‘]=indlen;
                }
                else{
                        s.maz[i/3][i%3]=9;
                        s.x=i/3;
                        s.y=i%3;
                        s.ind=i;
                }
        }
        if(scanf("%s",buff)!=1)return false;
        for(int i=0;i<9;i++){
                if(buff[i]>=‘0‘&&buff[i]<=‘9‘){
                        e.maz[i/3][i%3]=ind[buff[i]-‘0‘];
                }
                else{
                        e.maz[i/3][i%3]=9;
                        e.x=i/3;
                        e.y=i%3;
                }
        }
        e.ind=s.ind;//起始状态相同
        des=e.gethashcode();
        return true;
}
void bfs(){
        while(!que.empty()){
                pnt tp=que.front();que.pop();
                int x=tp.x,y=tp.y;
                for(int i=0;i<4;i++){
                        int tx=x+dx[i],ty=y+dy[i];
                        if(in(tx,ty)){
                                pnt nw=tp;
                                swap(nw.maz[tx][ty],nw.maz[x][y]);
                                nw.x=tx,nw.y=ty;
                                if(vis[nw.gethashcode()]==-1){
                                        vis[nw.hashCode]=i;
                                        pre[nw.hashCode]=tp.gethashcode();
                                        que.push(nw);
                                }
                        }
                }
        }
}
void init(){
        memset(vis,-1,sizeof(vis));
        memset(pre,-1,sizeof(pre));
        pnt s1=pnt("12345678X");//9种起始状态加入序列
        vis[s1.gethashcode()]=-2;
        que.push(s1);
        pnt s2=pnt("1234567X8");
        vis[s2.gethashcode()]=-2;
        que.push(s2);
        pnt s3=pnt("123456X78");
        vis[s3.gethashcode()]=-2;
        que.push(s3);
        pnt s4=pnt("12345X678");
        vis[s4.gethashcode()]=-2;
        que.push(s4);
        pnt s5=pnt("1234X5678");
        vis[s5.gethashcode()]=-2;
        que.push(s5);
        pnt s6=pnt("123X45678");
        vis[s6.gethashcode()]=-2;
        que.push(s6);
        pnt s7=pnt("12X345678");
        vis[s7.gethashcode()]=-2;
        que.push(s7);
        pnt s8=pnt("1X2345678");
        vis[s8.gethashcode()]=-2;
        que.push(s8);
        pnt s9=pnt("X12345678");
        vis[s9.gethashcode()]=-2;
        que.push(s9);
        bfs();
}
int heap[maxh],sz;
int getans(pnt e){//逆序遍历
        sz=0;
        int last=e.gethashcode();
        while(pre[last]>=0){
                heap[sz++]=vis[last];
                last=pre[last];
        }
        return sz;
}
void print(){
        for(int i=sz-1;i>=0;i--){
                switch(heap[i]){
                case 0:
                        putchar(‘d‘);break;
                case 1:
                        putchar(‘l‘);break;
                case 2:
                        putchar(‘r‘);break;
                case 3:
                        putchar(‘u‘);break;
                }
        }
        putchar(‘\n‘);
}
int main(){
        int T;
        scanf("%d",&T);
        init();
        for(int ti=1;ti<=T;ti++){
                input();
                s.hashCode=s.gethashcode();
                if(s.hashCode==des){
                        printf("Case %d: 0\n",ti);
                        puts("");
                        continue;
                }
                vis[s.hashCode]=-2;
                int step=getans(e);
                printf("Case %d: %d\n",ti,step);
                print();
        }
        return 0;
}

  

时间: 2024-12-20 02:05:58

HDU 3567 Eight II 打表,康托展开,bfs,g++提交可过c++不可过 难度:3的相关文章

HDU 3567 Eight II(八数码 II)

p.MsoNormal { margin: 0pt; margin-bottom: .0001pt; text-align: justify; font-family: Calibri; font-size: 10.5000pt } h1 { margin-top: 5.0000pt; margin-bottom: 5.0000pt; text-align: center; font-family: 宋体; color: rgb(26,92,200); font-weight: bold; fo

HDU 1043 Eight(反向BFS+打表+康托展开)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 题目大意:传统八数码问题 解题思路:就是从“12345678x”这个终点状态开始反向BFS,将各个状态记录下来,因为数字太大所以用康托展开将数字离散化. 代码: 1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<st

HDU1430 BFS + 打表 + 康托展开

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1430 , 一道比较好的题. 这道题要用到很多知识,康托展开.BFS.打表的预处理还要用到一一映射,做完受益匪浅. 其实这道题也可以用双向BFS来写,思路也已经有了,过几天再来写. 本文持续更新. 先说搜索部分: 对于魔板的每一个状态,都可以进行A.B.C三种操作,所以按照图论来讲,就是操作前的状态可以到达操作后的状态,所以就这样转换成了广搜问题. 这里要注意一点,由于题目要求字典序最小的,所以搜索的

HDU 1430 魔板(康托展开+BFS+预处理)

魔板 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 2921    Accepted Submission(s): 649 Problem Description 在魔方风靡全球之后不久,Rubik先生发明了它的简化版——魔板.魔板由8个同样大小的方块组成,每个方块颜色均不相同,可用数字1-8分别表示.任一时刻魔板的状态可用方块的颜

HDU - 1430 - 魔板( 康托展开 + BFS预处理 )

魔板 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4242    Accepted Submission(s): 1011 Problem Description 在魔方风靡全球之后不久,Rubik先生发明了它的简化版--魔板.魔板由8个同样大小的方块组成,每个方块颜色均不相同,可用数字1-8分别表示.任一时刻魔板的状态可用方块的颜

Aizu 0121 Seven Puzzle (康托展开+bfs)

Seven Puzzle Time Limit : 1 sec, Memory Limit : 65536 KB 7パズルは8つの正方形のカードとこれらのカードがぴたりと収まる枠を使って行います.それぞれのカードは互いに区別できるように.0,1,2....7と番号がつけられています.枠には.縦に2個.横に4個のカードを並べることができます. 7パズルを始めるときには.まず枠にすべてのカードを入れます.枠のなかで0のカードだけは.上下左右に隣接するカードと位置を交換することができます.たとえば.枠

POJ 1077 Eight(康托展开+BFS)

Eight Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 30176   Accepted: 13119   Special Judge Description The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15

HDU 3567 Eight II

Eight II Time Limit: 2000ms Memory Limit: 65536KB This problem will be judged on HDU. Original ID: 356764-bit integer IO format: %I64d      Java class name: Main Eight-puzzle, which is also called "Nine grids", comes from an old game. In this ga

HDU 3567 Eight II BFS预处理

题意:就是八数码问题,给你开始的串和结束的串,问你从开始到结束的最短且最小的变换序列是什么 分析:我们可以预处理打表,这里的这个题可以和HDU1430魔板那个题采取一样的做法 预处理打表,因为八数码问题实际上是每个小块位置的变化,上面的数字只是用来标记位置的, 所以通过映射将初末序列进行置换就好了,然后因为每次的x字符的置换位置不一样 所以需要以123456789这个初始串打9遍表就好了733ms #include <iostream> #include <cstdio> #inc