1. 程式人生 > >題解 P1949 【聰明的打字員_NOI導刊2011提高(10)】

題解 P1949 【聰明的打字員_NOI導刊2011提高(10)】

題目描述

阿蘭是某機密部門的打字員,她現在接到一個任務:需要在一天之內輸入幾百個長度固定為6的密碼。當然,她希望輸入的過程中敲擊鍵盤的總次數越少越好。

不幸的是,出於保密的需要,該部門用於輸入密碼的鍵盤是特殊設計的,鍵盤上沒有數字鍵,而只有以下六個鍵:swap0,swap1,up,down,left,right。為了說明這6個鍵的作用,我們先定義錄入區的6個位置的編號,從左至右依次為1,2,3,4,5,6。下面列出每個鍵的作用:

swap0:按swap0,游標位置不變,將游標所在的位置的數字與錄入區的1號位置的數字(左起第一個數字)交換。如果游標已經處在錄入區的1號位置,則按swap0鍵之後錄入區的數字不變。

swap1:按swap1,游標位置不變,將游標所在位置的數字與錄入區的6號位置的數字(左起第六個數字)交換。如果游標已經處在錄入區的6號位置,則按swap1鍵之後錄入區的數字不變。

up:按up,游標位置不變,講游標所在位置的數字加1(除非該數字是9)。例如,如果游標所在位置的數字為2,按up之後,該處的數字變為3;如果游標所在位置的數字為9,按up之後,該處的數字不變,游標位置也不變;

down:按down,游標位置不變,講游標所在位置的數字減1(除非該數字是0)。如果游標所在位置的數字為0,按down之後,該處的數字不變,游標位置也不變;

left:按left,游標左移一個位置,如果游標已在錄入區的1號位置(左起第一個位置)上,則游標不動;

right:按right,游標右移一個位置,如果游標已在錄入區的6號位置(左起第六個位置)上,則游標不動;

當然,為了使這樣的鍵盤發揮作用,每次錄入密碼之前,錄入區總會隨機出現一個長度為6的初始密碼,而且游標會固定出現在1號位置上。當巧妙的使用上述六個特殊鍵之後,可以得到目標密碼,這時游標允許停留在任何一個位置。

現在,阿蘭需要你的幫助,編寫一個程式,求出錄入一個密碼需要的最少的擊鍵次數。

輸入輸出格式

輸入格式:

僅一行,含有兩個長度為6的數,前者為初始密碼,後者為目標密碼,兩個密碼之間用一個空格隔開。

輸出格式:

僅一行,含有一個正整數,為最少需要的擊鍵次數。

輸入輸出樣例

輸入樣例#1:

123456 654321

輸出樣例#1:

11

主要思路:BFS + 迴圈佇列

這個題的數只有6位,我們直接當六位數存不就好了

首先不能寫DFS,明顯這題BFS要比DFS更優(DFS不就退化成了窮舉嗎(大霧))

swap0,swap1,up,down操作,直接模擬就好,left和right就更好辦了,直接更新游標左右就好。

code#1:36分(肯定TLE了)

福利:自帶野生debug程式碼

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define go(i,j,n,k) for(int i=j;i<=n;i+=k)
#define fo(i,j,n,k) for(int i=j;i>=n;i-=k)
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-f;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void fre() {
    freopen(".in", "r", stdin);
    freopen(".out", "w", stdout);
}
int st, ed;
int vis[1000100][7]; // 判重陣列

inline int swap0(int x, int now) {
    int res = 1;
    now = 6 - now + 1;
    go(i, 1, now - 1, 1) res *= 10;
    int xx = (x / res) % 10;
    int yy = (x / 100000);
    int ans = x;
    ans -= xx * res;
    ans -= yy * 100000;
    ans += xx * 100000;
    ans += yy * res;
    return ans;
}
inline int swap1(int x, int now) {
    int res = 1;
    now = 6 - now + 1;
    go(i, 1, now - 1, 1) res *= 10;
    int xx = (x / res) % 10;
    int yy = x % 10;
    int ans = x;
    ans -= xx * res;
    ans -= yy;
    ans += xx;
    ans += yy * res;
    return ans;
}
inline int up(int x, int now) {
    int res = 1;
    now = 6 - now + 1;
    go(i, 1, now - 1, 1) res *= 10;
    int ju = (x / res) % 10;
    if(ju == 9) return x;
    return x + res;
}
inline int down(int x, int now) {
    int res = 1;
    now = 6 - now + 1;
    go(i, 1, now - 1, 1) res *= 10;
    int ju = (x / res) % 10;
    if(ju == 0) return x;
    return x - res;
}
// 四種操作
inline void debug() {
    puts("debug模式:");
    puts("1.swap0  2.swap1");
    puts("3.up     4.down");
    int s = read(), st = read(), now = read();
    if(s == 1) {
        cout << swap0(st, now) << "\n";
    } else if(s == 2) {
        cout << swap1(st, now) << "\n";
    } else if(s == 3) {
        cout << up(st, now) << "\n";
    } else if(s == 4) {
        cout << down(st, now) << "\n";
    }
}

struct node{
    int x, now, dep;
    node(int _x = 0, int _now = 0, int _dep = 0) : x(_x), now(_now), dep(_dep) {}
    node() : node(0, 0, 0) {}
};
queue<node> q;
inline int bfs(int st, int ed) {
    q.push(node(st, 1, 0));
    while(!q.empty()) {
        node get = q.front(); q.pop();
        int oo, x = get.x, now = get.now, deep = get.dep;
        vis[x][now] = 1;
        if(x == ed)
            return deep;
        oo = swap0(x, now);
        if(!vis[oo][now]) q.push(node(oo, now, deep + 1));
        oo = swap1(x, now);
        if(!vis[oo][now]) q.push(node(oo, now, deep + 1));
        oo = up(x, now);
        if(!vis[oo][now]) q.push(node(oo, now, deep + 1));
        oo = down(x, now);
        if(!vis[oo][now]) q.push(node(oo, now, deep + 1));
        if(now > 1 && !vis[x][now - 1]) q.push(node(x, now - 1, deep + 1)); // 注意這裡的特判!now不能越界!
        if(now < 6 && !vis[x][now + 1]) q.push(node(x, now + 1, deep + 1)); // 注意這裡的特判!now不能越界!
    }
}

int main(){
    //fre();
    // while(1) 
    //  debug();
    st = read(), ed = read();
    if(st == ed) {
        cout << "0\n";
        return 0;
    }
    cout << bfs(st, ed) << "\n";
    return 0;
}

為什麼會TLE?

首先,儘管我們在入隊之前就已經判重了,但是這個程式碼會重複插一樣的點,所以我們可以在從佇列中取出時來再次判重。

而且,眾所周知,有種說法說STL很慢,所以我好奇的自己手寫了個佇列。

code#2:72分(這次WA了)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define go(i,j,n,k) for(int i=j;i<=n;i+=k)
#define fo(i,j,n,k) for(int i=j;i>=n;i-=k)
#define mn 10000100
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-f;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void fre() {
    freopen(".in", "r", stdin);
    freopen(".out", "w", stdout);
}
int st, ed;
bool vis[1000100][7]; // 判重陣列 
int ans = inf;
int ress[7] = {0, 1, 10, 100, 1000, 10000, 100000};

inline int swap0(int x, int now) {
    now = 6 - now + 1;
    int res = ress[now];
    int xx = (x / res) % 10;
    int yy = (x / 100000);
    int ans = x;
    ans -= xx * res;
    ans -= yy * 100000;
    ans += xx * 100000;
    ans += yy * res;
    return ans;
}
inline int swap1(int x, int now) {
    now = 6 - now + 1;
    int res = ress[now];
    int xx = (x / res) % 10;
    int yy = x % 10;
    int ans = x;
    ans -= xx * res;
    ans -= yy;
    ans += xx;
    ans += yy * res;
    return ans;
}
inline int up(int x, int now) {
    now = 6 - now + 1;
    int res = ress[now];
    int ju = (x / res) % 10;
    if(ju == 9) return x;
    return x + res;
}
inline int down(int x, int now) {
    now = 6 - now + 1;
    int res = ress[now];
    int ju = (x / res) % 10;
    if(ju == 0) return x;
    return x - res;
}

inline void debug() {
    puts("debug模式:");
    puts("1.swap0  2.swap1");
    puts("3.up     4.down");
    int s = read(), st = read(), now = read();
    if(s == 1) {
        cout << swap0(st, now) << "\n";
    } else if(s == 2) {
        cout << swap1(st, now) << "\n";
    } else if(s == 3) {
        cout << up(st, now) << "\n";
    } else if(s == 4) {
        cout << down(st, now) << "\n";
    }
}
// 手寫佇列哦QwQ
int X[mn], Now[mn], dep[mn], head = 1, tail = 0;
int x, now, oo, deep;
inline int bfs(int st, int ed) {
    ++tail, X[tail] = st, Now[tail] = 1, dep[tail] = 0;
    while(head <= tail) {
        x = X[head], now = Now[head], deep = dep[head]; head++;
        // cout << x << " " << now << " " << deep << "\n";
        if(vis[x][now]) continue; // 呼叫前再次判重
        vis[x][now] = 1;
        if(x == ed) return deep;
        oo = swap0(x, now);
        if(!vis[oo][now]) ++tail, X[tail] = oo, Now[tail] = now, dep[tail] = deep + 1;
        oo = swap1(x, now);
        if(!vis[oo][now]) ++tail, X[tail] = oo, Now[tail] = now, dep[tail] = deep + 1;
        oo = up(x, now);
        if(!vis[oo][now]) ++tail, X[tail] = oo, Now[tail] = now, dep[tail] = deep + 1;
        oo = down(x, now);
        if(!vis[oo][now]) ++tail, X[tail] = oo, Now[tail] = now, dep[tail] = deep + 1;
        if(now > 1 && !vis[x][now - 1]) ++tail, X[tail] = x, Now[tail] = now - 1, dep[tail] = deep + 1;
        if(now < 6 && !vis[x][now + 1]) ++tail, X[tail] = x, Now[tail] = now + 1, dep[tail] = deep + 1;
    }
    return deep + 1;
}
int main(){
    st = read(), ed = read();
    if(st == ed) {
        cout << "0\n";
        return 0;
    }
    cout << bfs(st, ed) << "\n";
    return 0;
}

怎麼還不AC

我們可以試著把陣列開大點,咦?多了9分?

好像是陣列大小??但是我的空間已經開到最大了啊

這個時候就可以用迴圈佇列卡空間了

具體寫法看程式碼

code#3:100分

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define go(i,j,n,k) for(int i=j;i<=n;i+=k)
#define fo(i,j,n,k) for(int i=j;i>=n;i-=k)
#define mn 10000100
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-f;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void fre() {
    freopen(".in", "r", stdin);
    freopen(".out", "w", stdout);
}
int st, ed;
bool vis[1000100][7]; // 判重陣列 
int ress[7] = {0, 1, 10, 100, 1000, 10000, 100000};
int res, xx, yy, ans, ju;
inline int swap0(int x, int now) {
    now = 6 - now + 1;
    res = ress[now];
    xx = (x / res) % 10;
    yy = (x / 100000);
    ans = x;
    ans -= xx * res;
    ans -= yy * 100000;
    ans += xx * 100000;
    ans += yy * res;
    return ans;
}
inline int swap1(int x, int now) {
    now = 6 - now + 1;
    res = ress[now];
    xx = (x / res) % 10;
    yy = x % 10;
    ans = x;
    ans -= xx * res;
    ans -= yy;
    ans += xx;
    ans += yy * res;
    return ans;
}
inline int up(int x, int now) {
    now = 6 - now + 1;
    res = ress[now];
    ju = (x / res) % 10;
    if(ju == 9) return x;
    return x + res;
}
inline int down(int x, int now) {
    now = 6 - now + 1;
    res = ress[now];
    ju = (x / res) % 10;
    if(ju == 0) return x;
    return x - res;
}

inline void debug() {
    puts("debug模式:");
    puts("1.swap0  2.swap1");
    puts("3.up     4.down");
    int s = read(), st = read(), now = read();
    if(s == 1) {
        cout << swap0(st, now) << "\n";
    } else if(s == 2) {
        cout << swap1(st, now) << "\n";
    } else if(s == 3) {
        cout << up(st, now) << "\n";
    } else if(s == 4) {
        cout << down(st, now) << "\n";
    }
}

int X[mn], Now[mn], dep[mn], head = 0, tail = 0;
int x, now, oo, deep;
inline int bfs(int st, int ed) {
    ++tail, X[tail] = st, Now[tail] = 1, dep[tail] = 0;
    while(head != tail) { // 這裡就不能是head <= tail了
        ++head;
        if(head > 10000000) head = 0; // 記得迴圈
        x = X[head], now = Now[head], deep = dep[head]; 
        // cout << x << " " << now << " " << deep << "\n";
        if(x == ed) return deep;
        if(vis[x][now]) continue;
        vis[x][now] = 1;
        oo = swap0(x, now);
        if(!vis[oo][now]) {   // 都要迴圈的QAQ
            if(++tail > 10000000) tail = 0;
            X[tail] = oo, Now[tail] = now, dep[tail] = deep + 1;
        }
        oo = swap1(x, now);
        if(!vis[oo][now]) {
            if(++tail > 10000000) tail = 0;
            X[tail] = oo, Now[tail] = now, dep[tail] = deep + 1;
        }
        oo = up(x, now);
        if(!vis[oo][now]) {
            if(++tail > 10000000) tail = 0;
            X[tail] = oo, Now[tail] = now, dep[tail] = deep + 1;
        }
        oo = down(x, now);
        if(!vis[oo][now]) {
            if(++tail > 10000000) tail = 0;
            X[tail] = oo, Now[tail] = now, dep[tail] = deep + 1;
        }
        if(now > 1 && !vis[x][now - 1]) {
            if(++tail > 10000000) tail = 0; 
            X[tail] = x, Now[tail] = now - 1, dep[tail] = deep + 1;
        }
        if(now < 6 && !vis[x][now + 1]) {
            if(++tail > 10000000) tail = 0; 
            X[tail] = x, Now[tail] = now + 1, dep[tail] = deep + 1;
        }
    }
    return deep + 1;
}

int main(){
    st = read(), ed = read();
    if(st == ed) {
        cout << "0\n";
        return 0;
    }
    cout << bfs(st, ed) << "\n";
    return 0;
}

希望可以幫到被卡空間的同學