POJ 聪明的打字员

题目: LINK

通过给定的六种操作将一个六位数变为另一个六位数,求需要的最少操作数。
六种操作:
    左移和右移:将光标位置左移一位或右移一位,在第一位时无法左移,最后一位时无法右移。
    左交换和右交换:将光标位置的数字与第一位或最后一位交换
    增大或减小:将光标位置的数字增大或减小1


最容易想到的做法是直接BFS,6*1000000 个状态,妥妥TLE。
可以把六种操作分成两种,一种是左右移和左右交换,另一种是增加减小。为什么区分为两种?
因为,我们可以先进行第一种操作, 只有位置交换和光标移动,不会有数值的变化,所以我们可以BFS遍历出来所有的位置组合6!和每种下的光标经历的情况.
对于光标经历过的位置的情况有一下10种.
1
1,2
1,2,3
1,2,3,4
1,2,3,4,5
1,6
1,2,6
1,2,3,6
1,2,3,4,6
1,2,3,4,5,6
所以状态是6*6!*10.是可以接受的,而且这一步可以预处理出来, 与出入的数据无关.
对于这儿6!,可以直接用6进制进行处理,当然也可以利用康托展开进行HASH。


对于分离出来的第二种操作,由第一种的状态,如果某一位光标经历过(与什么时候经历的无关),那么它的值可以改变,改变的大小就是这一位两者先后的差值.
对于第一种得出的所有的状态算出最小的移动次数就好了.

#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
//typedef __int64 LL; 
struct node {
    int num[7], has, th, step; 
}; 
bool vis[51111][12][7]; 
char s1[10], s2[10]; 
vector<node > save; 
int ha[10], T[10]; 
int get(int num[]) {
    int ret = 0; 
    for(int i = 0; i < 6; i++) {
        ret *= 6; ret += num[i]; 
    }
    return ret; 
}
void change(node &in, int ii) {
    if(ii == 0) {//left move
        if(in.th) in.th --; 
    }
    else if(ii == 1) {//right move
        if(in.th != 5) in.th ++; 
        if(in.th == 5) {
            if(in.has <= 4) in.has += 5; 
        }
        else {
            if(in.has <=4 && in.th > in.has) in.has ++; 
            if(in.has >=5 && in.th > in.has - 5) in.has ++; 
        }
    }
    else if(ii == 2) { //left swap
        swap(in.num[0], in.num[in.th]); 
    }
    else if(ii == 3) {//right swap  
        swap(in.num[5], in.num[in.th]); 
        if(in.has < 5) in.has += 5; 
    }
    in.step ++; 
}
void bfs() {
    queue< node > Q; 
    save.clear(); 
    memset(vis, 0, sizeof(vis)); 
    node now, next; 
    for(int i = 0; i < 6; i++) now.num[i] = i; 
    now.has = 0;  now.th = 0; now.step = 0; 
    vis[get(now.num)][now.has][now.th] = 1; 
    Q.push(now); 
    while(!Q.empty()) {
        now = Q.front(); Q.pop(); 
        save.push_back(now); 
        for(int i = 0; i < 4; i++) {
            next = now; 
            change(next, i); 
            int tmp = get(next.num); 
            if(vis[tmp][next.has][next.th]) continue; 
            vis[tmp][next.has][next.th] = 1; 
            Q.push(next); 
        }
    }
}
void sol() {
    int ret = INF; 
    for(int i = 0; i < save.size(); i++) {
        int flag = 0,sum = 0; 
        int tt = save[i].has; 
        if(tt > 4) tt -= 5; 
        for(int j= 0; j < 6; j++) {
            int dd = 0;  
            if(j <= 4 && j <= tt)  dd = 1;  
            else if(j == 5 && save[i].has >= 5) dd = 1; 
            int pre = save[i].num[j]; 
            if(dd == 1 )  sum += abs(s1[pre] - s2[j]) ;  
            else if(dd == 0 && s1[pre] != s2[j]) {
                flag = 1; break; 
            }
        }
        if(!flag) ret = min(ret, save[i].step + sum); 
    }
    printf("%d\n", ret); 
}
int main() 
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin); 
#endif // ONLINE_JUDGE
    bfs(); 
    while(scanf("%s%s", s1, s2) != EOF) {
        for(int i = 0; i < 6; i++) s1[i] -= '0', s2[i] -= '0'; 
        sol(); 
    }
    return 0; 
}  


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值