USACO 6.5.4 The Clocks BFS

http://train.usaco.org/usacoprob2?a=BI4LEPNXcEa&S=clocks

题目大意:给九个钟的初始状态,指针只可能指向3,6,9,12,有九种操作,每种操作拨动一个固定组合的种顺时针45°,求最少操作能够把所有钟拨到12的情况(操作数相同时,要操作的数字小),按顺序输出




一开始觉得每次有九种选择的可能性,又要输出操作数最少的情况,就想到了用IDDFS,但是在第七个样例超时。为了处理结果操作数比较大的情况,想到了二分,但是然并卵,因为操作数不满足二分性,小的操作数下能够完成并不代表大的操作数必定能够完成

随后迫于无奈使用了BFS,没想到竟然过了,可能因为哈希查重的关系队列并不会变得很长


记录操作我一开始用的string类,但是会超时,就改成了前驱数组。哈希是将钟的情况用一个四进制数表示,直接用数组记录

/*
ID: frontie1
TASK: clocks
LANG: C++
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

bool visited[270000];
int pre[270000];
int log[270000];
int table[15];
int oper[10][6] = {{1,2,4,5}, {1,2,3}, {2,3,5,6}, {1,4,7}, {2,4,5,6,8}, {3,6,9}, {4,5,7,8}, {7,8,9}, {5,6,8,9}};
int oper_num[10] = {4, 3, 4, 3, 5, 3, 4, 3, 4};
queue<int> Q;

int encode()
{
    int output = 0;
    for(int i = 1; i <= 9; ++i){
        output <<= 2;
        output += table[i];
    }
    return output;
}

void decode(int num)
{
    for(int i = 9; i >= 1; --i){
        table[i] = num%4;
        num >>= 2;
    }
}


void BFS()
{
    Q.push(encode());
    pre[encode()] = -1;
    visited[encode()] = true;
    int cur, nxt;

    while(!Q.empty()){
        cur = Q.front(); Q.pop();
        decode(cur);
        for(int ope = 0; ope < 9; ++ope){
            for(int i = 0; i < oper_num[ope]; ++i){
                ++table[oper[ope][i]];
                table[oper[ope][i]] %= 4;
            }
            nxt = encode();
            if(!visited[nxt]){
                visited[nxt] = true;
                pre[nxt] = cur;
                log[nxt] = ope+1;
                if(nxt == 0) return;
                Q.push(nxt);
            }
            for(int i = 0; i < oper_num[ope]; ++i){
                table[oper[ope][i]] += 3;
                table[oper[ope][i]] %= 4;
            }
        }
    }
}

void print_num(int num)
{
    if(pre[num] == -1) return;
    print_num(pre[num]);
    cout << log[num];
    if(num != 0) cout << ' ';
}

int main()
{
    freopen("clocks.in", "r", stdin);
    freopen("clocks.out", "w", stdout);

    for(int i = 1; i <= 9; ++i){
        cin >> table[i];
        table[i] /= 3;
        table[i] %= 4;
    }

    int ori = encode();

    BFS();

    print_num(0);
    cout << endl;

    return 0;
}


附录:IDDFS的做法(会超时的)

/*
ID: frontie1
TASK: clocks
LANG: C++
*/
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

bool visited[40][270000];
int table[15];
int oper[10][6] = {{1,2,4,5}, {1,2,3}, {2,3,5,6}, {1,4,7}, {2,4,5,6,8}, {3,6,9}, {4,5,7,8}, {7,8,9}, {5,6,8,9}};
int oper_num[10] = {4, 3, 4, 3, 5, 3, 4, 3, 4};
int step;
int answer[100];

void printtable()
{
    for(int i = 1; i <= 9; ++i){
        cout << table[i] << ' ';
    }
    cout << endl;
}

int encode()
{
    int output = 0;
    for(int i = 1; i <= 9; ++i){
        output <<= 2;
        output += table[i];
    }
    return output;
}

bool DFS(int dep, int ope)
{
    if(dep == step){
        for(int i = 1; i <= 9; ++i){
            if(table[i] != 0) return false;
        }
        return true;
    }
    for(int i = 0; i < oper_num[ope]; ++i){
        ++table[oper[ope][i]];
        table[oper[ope][i]] %= 4;
    }
    int code = encode();
    if(!visited[step-dep-1][code]){
        answer[dep] = ope;
        visited[step-dep-1][code] = true;
        for(int i = 0; i < 9; ++i){
            if(DFS(dep+1, i)) return true;
        }
    }
    for(int i = 0; i < oper_num[ope]; ++i){
        table[oper[ope][i]] += 3;
        table[oper[ope][i]] %= 4;
    }
    return false;
}

bool DFS_trigger()
{
    for(int i = 0; i < 9; ++i){
        if(DFS(0, i)) return true;
    }
    return false;
}

bool BS(int lo, int hi)
{
    if(lo > hi) return false;
    int mid = ((lo+hi)>>1);
    if(BS(lo, mid-1)) return true;
    step = mid;
    if(DFS_trigger()) return true;
    BS(mid+1, hi);

}

int main()
{
    freopen("clocks.in", "r", stdin);
    freopen("clocks.out", "w", stdout);

    for(int i = 1; i <= 9; ++i){
        cin >> table[i];
        table[i] /= 3;
        table[i] %= 4;
    }


//    while(!DFS_trigger()){
//        ++step;
//    }

    BS(1, 30);

    for(int i = 0; i < step; ++i){
        cout << answer[i]+1;
        if(i != step-1) cout << ' ';
    }
    cout << endl;

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值