ZOJ 3814 / 2014 牡丹江赛区网络赛 F. Sawtooth Puzzle

题目: LINK

题目给出了 起始 和 最终的两张图, 每个图有9小块, 每个小块有对应图案,而且 每个小块的边有可能有齿轮
每一步是顺时针转动一个小块,对于和它相邻的小块,如果这两块接触的两条边都含有齿轮,就会带动相邻的小块转动,被带动的小块有可能会继续带动其他小块。
求 最少多少步可以由起始图 得到目的图.
BFS,不过有点繁琐。
要注意,1,状态的记录,不能把每个小块的图案压缩8*8*9,每次去判断,虽然没有这样试着去写,应该妥妥过不掉。每个小块有4种状态,即转动0,1,2,3次,对于9个小块 4^9个状态,可以接受。
2, 每次转动后, 每块的每条边也会跟着转动,也就是图中齿轮的情况会变化。
3, 每次判断是否可以达到目的图时,要判断当前每块的转动状态是否和目标一样。
代码写的比较挫。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std; 
#define INF 1000000000
char S[11][10][10], T[11][10][10]; 
int t, nowv[4][4], dir[4][2]= {0, -1, -1, 0, 0, 1, 1, 0}; 
bool vv[4][4], vis[266666]; 
int G[11][5]; 
struct node {
    int ddir[10], cut[10], step; 
}; 
node SS, TT; 
int get(int ddir[]) {//对于9个块的0123的状态进行压缩,看做4进制.
    int ret = 0; 
    for(int i = 1; i <= 9; i++) {
        ret <<= 2; ret |= ddir[i]; 
    }
    return ret; 
}
int equ(node x, node y) {//判断当前的状态和目标状态是否吻合,即当前的转动位置是否吻合目标
    for(int i = 1; i<= 9; i++) {
        if((1<<(3 - x.ddir[i])) & y.ddir[i]) continue; 
        return 0; 
    }
    return 1; 
}
void dfs(int x,int y, int ad) {
    nowv[x][y] = ad; 
    for(int i = 0; i < 4; i++) {
        int nx = x + dir[i][0]; 
        int ny = y + dir[i][1]; 
        if(nx < 1 || nx > 3 || ny < 1 || ny > 3 || vv[nx][ny]) continue; 
        if(G[(x-1)*3 + y][i] == 0 || G[(nx - 1)*3 + ny][(i+2)%4] == 0) continue; 
        vv[nx][ny] = 1; 
        dfs(nx, ny, ad ==1 ? -1: 1); 
    }
}
void change(node &in, int x, int y) {
    memset(vv, 0, sizeof(vv)); 
    memset(nowv, 0, sizeof(nowv)); // maybe not change.
    for(int i = 1; i <= 9; i++) {
        for(int j = 0; j < 4; j++) {
            int ww = (in.cut[i] >> j) & 1; 
            G[i][3-j] = ww; //获得当前 各块的每条边是否有齿轮
        }
    }
    int xx = (x-1) / 3 + 1; 
    int yy = (x-1) % 3 + 1; 
    vv[xx][yy] = 1; 
    dfs(xx, yy, y); //搜索 确定9块是否是否转动
    for(int i =1; i <= 3; i++) {// 
        for(int j = 1; j <= 3; j++) {
            int id = (i-1)*3 + j; 
            in.ddir[id] = (in.ddir[id] + nowv[i][j]); //修改每块的转动后的新状态0123
            if(in.ddir[id] == -1) in.ddir[id] = 3; 
            else if(in.ddir[id] == 4) in.ddir[id] = 0; 
            if(nowv[i][j] == 1) {//更新转动后每块每条边是否含有齿轮
                int gg = in.cut[id] &1; 
                in.cut[id] >>= 1; 
                in.cut[id] |= (gg << 3); 
            }
            else if(nowv[i][j] == -1) {
                int gg = (in.cut[id] >> 3) &1; 
                in.cut[id] &= 7; 
                in.cut[id] <<= 1; 
                in.cut[id] |= gg; 
            }
        }
    }
}
int bfs() {
    queue<node > Q; 
    memset(vis, 0, sizeof(vis)); 
    for(int i = 1; i<=9; i++) SS.ddir[i] = 0; 
    SS.step = 0; 
    Q.push(SS); 
    vis[get(SS.ddir)] = 1; 
    node now, next; 
    while(!Q.empty()) {
        now = Q.front(); Q.pop(); 
        if(equ(now, TT)) return now.step;  
        for(int i = 1; i<= 9; i++) {
            next = now; 
            next.step ++; 
            change(next, i, 1); //对于i-th块转动
            int tmp = get(next.ddir); 
            if(vis[tmp]) continue; 
            vis[tmp] = 1;
            Q.push(next); 
        }
    }
    return -1; 
}
int gao(int x) {//一共四种旋转,得到可有哪些旋转得到,压缩到4位二进制里面 .
    int ret = 0; 
    int flag = 0; 
    for(int i = 1; i <= 8; i++) {
        for(int j = 1; j <= 8; j++) {
            if(S[x][i][j] != T[x][i][j]) {
                flag = 1; break; 
            }
        }
        if(flag ) break; 
    }
    ret <<= 1; 
    if(!flag)  ret |= 1; 
    flag = 0; 
    for(int i = 1; i <= 8; i++) {
        for(int j = 1; j <= 8; j++) {
            if(S[x][i][j] != T[x][j][9 - i]) {
                flag = 1; break; 
            }
        }
        if(flag) break; 
    }
    ret <<= 1; 
    if(!flag) ret |= 1; 
    flag = 0; 
    for(int i = 1; i <= 8; i++) {
        for(int j = 1; j <= 8; j++) {
            if(S[x][i][j] != T[x][9-i][9-j]) {
                flag = 1; break; 
            }
        }
        if(flag) break; 
    }
    ret <<= 1; 
    if(!flag) ret |= 1; 
    flag = 0; 
    for(int i = 1; i <= 8; i++) {
        for(int j = 1; j <= 8; j++) {
            if(S[x][i][j] != T[x][9-j][i]) {
                flag = 1; break; 
            }
        }
        if(flag) break; 
    }
    ret <<= 1; 
    if(!flag) ret |= 1; 
    return ret; 
}
int main() 
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin); 
#endif // ONLINE_JUDGE
    scanf("%d", &t); 
    while(t--) {
        for(int i = 1; i<=24; i++) {
            for(int j = 1; j<=3; j++) {
                int ii = (i-1)/8 ; 
                ii = ii*3 + j; 
                scanf("%s", S[ii][(i-1)%8 + 1]+1); 
            }
        }
        for(int i = 1; i <= 24; i++) {
            for(int j = 1; j <= 3; j++) {
                int ii = (i-1) / 8; 
                ii = ii* 3 + j; 
                scanf("%s", T[ii][(i-1)%8 + 1] + 1); 
            }
        }
        int tt; 
        for(int i = 1; i <= 9; i++) {
            SS.cut[i] = 0; 
           for(int j = 0; j < 4; j++) {
               scanf("%d", &tt); 
               SS.cut[i] <<= 1; SS.cut[i] |= tt;//把每一个小块4个边的状态记录
          }
        }
        int mark = 0; 
        for(int i = 1; i <= 9 ; i++) {
            TT.ddir[i] = gao(i); //求终态的每个块可以由起始状态怎样旋转得到.用4为二进制表示
            if(TT.ddir[i] == 0) {
                mark = 1; break; 
            }
        }
        if(mark) {
            puts("-1"); continue; 
        }
        int ans = bfs(); 
        printf("%d\n", ans); 
    }
    return 0; 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值