POJ -- 2965 The Pilots Brothers' refrigerator

題目連接: POJ -- 2965ios

題目給出的是4X4的矩陣,規模不算大。因爲問的是最少的操做步數,很容易想到bfs寬搜。一共16個格子,總共的狀態數是2^16,不算大,因此這個策略的確是對的。編程

可是須要注意到每一個「狀態」都是一個4x4的矩陣,咱們須要想辦法去記錄一個矩陣的狀態。用整數是一個直觀的方法,咱們每次對當前矩陣作過變形後,新的矩陣存儲於另外一個二維數組中,而後再算出表明新矩陣狀態的整數。我 們存儲這個整數就好。數組

可是這麼作是會超時的。緣由是這個寬搜的出度特別大,出度也是16(當前矩陣每改變一個元素都會生成一個新矩陣,一共16個元素)。加上咱們在矩陣和表明矩陣的整數之間作變換也須要消耗O(16)的時間,一共花掉了O(16^2)。一共2^16個狀態,咱們對每一個狀態作O(16^2)的操做,這個複雜度是大於10^7了,會超時。函數

解決的辦法是利用位運算。咱們將當前的矩陣用一個整數表明(和以前作的同樣),當對元素(i, j)做翻轉的時候,直接在整數上作文章就能完成(這個是能夠達到的,就是很麻煩),這樣不用在矩陣和整數之間來回轉,省下了O(16)的複雜度。優化

#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
typedef unsigned short ushort;
char g[10][10];
int vis[100000], path[100000], from[100000];
ushort q[100000];
int front, end;
ushort get_id(char g[10][10])
{
    ushort id = 0;
    for(int i = 0; i < 4; i++)
    {
        for(int j = 0; j < 4; j++)
        {
            id <<= 1;
            if(g[i][j] == '+') id |= 1;
        }
    }
    return id;
}

ushort black_mask_r[4] = { (((1 << 4) - 1) << (3*4)), (((1 << 4) - 1) << (2*4)), (((1 << 4) - 1) << (1*4)), ((1 << 4) - 1) };
ushort white_mask_r[4] = { ~(((1 << 4) - 1) << (3*4)), ~(((1 << 4) - 1) << (2*4)), ~(((1 << 4) - 1) << (1*4)), ~((1 << 4) - 1) };
ushort mark = (1 | (1 << 4) | (1 << 2*4) | (1 << 3*4));
ushort black_mask_c[4] = { (mark << 3), (mark << 2), (mark << 1), mark};
ushort white_mask_c[4] = { ~(mark << 3), ~(mark << 2), ~(mark << 1), ~mark};

ushort change_ij(ushort st, int i, int j)
{
    ushort r = (st & black_mask_r[i]);
    r = ~r;
    r &= black_mask_r[i];
    ushort restr = st & white_mask_r[i];
    r = restr | r;

    ushort k = ((1 << (3-j)) << (4*(3-i)));
    r ^= k;

    ushort c = r & black_mask_c[j];
    c = ~c;
    c &= black_mask_c[j];
    ushort restc = r & white_mask_c[j];
    c = restc | c;
    return c;
}

ushort id;
void print_path(ushort st, int l)
{
    if(from[st] < 0)
    {
        printf("%d\n", l);
        return ;
    }
    print_path(q[from[st]], l+1);
    printf("%d %d\n", path[st]/4+1, path[st]%4+1);
}

int main()
{
    // freopen("in.txt", "r", stdin);

    for(int i = 0; i < 4; i++)
    {
        scanf("%s", g[i]);
    }
    id = get_id(g);

    vis[id] = 1; q[end++] = id; from[id] = -1;
    ushort ans = -1;
    while(front != end)
    {
        int index = front;
        ushort x = q[front++];
        if(!x) { ans = x; break; }
        for(int i = 0; i < 4; i++)
        {
            for(int j = 0; j < 4; j++)
            {
                ushort st = change_ij(x, i, j);
                if(!vis[st])
                {
                    q[end++] = st;
                    vis[st] = 1;
                    path[st] = 4*i+j;
                    from[st] = index;
                }
            }
        }
    }

    if(!ans)
    {
        print_path(0, 0);
    }
    return 0;
}

位運算的代碼確實有點噁心,我編程能力差,調試了特別久。spa

代碼優化時間就是用change_ij函數去代替在矩陣數組和整數之間來回轉換,那個函數內部用的都是位運算,速度比4*4的循環快多了。調試

相關文章
相關標籤/搜索