USACO: clocks

这道题来自于IOI 94。
Consider nine clocks arranged in a 3x3 array thusly:
|-------|    |-------|    |-------|    
| | | | | | |
|---O | |---O | | O |
| | | | | |
|-------| |-------| |-------|
A B C

|-------| |-------| |-------|
| | | | | |
| O | | O | | O |
| | | | | | | | |
|-------| |-------| |-------|
D E F

|-------| |-------| |-------|
| | | | | |
| O | | O---| | O |
| | | | | | | |
|-------| |-------| |-------|
G H I

The goal is to find a minimal sequence of moves to return all the dials to 12 o'clock. Nine different ways to turn the dials on the clocks are spplied via a table below; each way is called a move. Select for each move a number 1 through 9 which will cause the dials of the affected clocks (see next table) to be turned 90 degrees clockwise.

Move Affected clocks
1 ABDE
2 ABC
3 BCEF
4 ADG
5 BDEFH
6 CFI
7 DEGH
8 GHI
9 EFHI

Example

Each number represents a time accoring to following table:
9 9 12       9 12 12       9 12 12        12 12 12      12 12 12 
6 6 6 5 -> 9 9 9 8-> 9 9 9 4 -> 12 9 9 9-> 12 12 12
6 3 6 6 6 6 9 9 9 12 9 9 12 12 12

[But this might or might not be the `correct' answer; see below.]

PROGRAM NAME: clocks

INPUT FORMAT

Lines 1-3: Three lines of three space-separated numbers; each number represents the start time of one clock, 3, 6, 9, or 12. The ordering of the numbers corresponds to the first example above.

SAMPLE INPUT (file clocks.in)

9 9 12
6 6 6
6 3 6

OUTPUT FORMAT

A single line that contains a space separated list of the shortest sequence of moves (designated by numbers) which returns all the clocks to 12:00. If there is more than one solution, print the one which gives the lowest number when the moves are concatenated (e.g., 5 2 4 6 < 9 3 1 1).

SAMPLE OUTPUT (file clocks.out)

4 5 8 9

分析

这道目的一种解法是——穷举。每种移动方法最多出现3次,超过3次就无意义了。所以总共有4^9=2^18=256K种可能。基本上用穷举在很短的时间内就可以求出来了。
  1. 由于每种移动方法最多出现三次,故用一个32位整数就可以表示当前的移动方法(用9×3个bit)。
  2. 利用递归进行穷举的时候,可以剪枝来减小穷举的量。
代码:

#include <stdio.h>
#include <assert.h>

int rules[10][9] = { {0,0,0,0,0,0,0,0,0}, {3,3,0,3,3,0,0,0,0}, {3,3,3,0,0,0,0,0,0},
    {0,3,3,0,3,3,0,0,0}, {3,0,0,3,0,0,3,0,0}, {0,3,0,3,3,3,0,3,0},
    {0,0,3,0,0,3,0,0,3}, {0,0,0,3,3,0,3,3,0}, {0,0,0,0,0,0,3,3,3},
    {0,0,0,0,3,3,0,3,3}};

int getMinMoves(int clocks[9], int rule, int curMove);
void print(FILE* fout, int minMove);

int main(void)
{
    int clocks[9];
    int i;
    int min_moves;
    FILE* fin = fopen("clocks.in", "r");
    FILE* fout = fopen("clocks.out", "w");
    assert(fin != NULL && fout != NULL);
       
    for (i = 0; i < 9; i ++)
    {
        fscanf(fin, "%d", clocks+i);
        if (clocks[i] == 12)
        {
            clocks[i] = 0;//for arithmetic convenience
        }
    }

    min_moves = getMinMoves(clocks, 1, 0);

    print(fout, min_moves);
    fclose(fin);
    fclose(fout);
    return 0;
}

int getMinMoves(int clocks[9], int rule, int curMove)
{
    int tempMove = 0;//record the search result of the subtree
    int minMove = 1 << 29;//record the mini of search results, initially set to a big value
    int curClocks[9];//current clocks
    int i,j;
   
    memcpy(curClocks, clocks, sizeof(int)*9);
    for (i = 0; i < 4; i ++) // at most move 3 times according to curren rule
    {
        if ( i != 0)//if move clocks according to the current rule
        {
            curMove = curMove | (1 << ((rule -1)*3 + i - 1));
            for (j = 0; j < 9; j ++)
            {
                curClocks[j] = (clocks[j] + rules[rule][j] * i) % 12;
            }
        }

        for (j = 0; j < 9 && curClocks[j] == 0; j ++)
        {
        }
        if (j == 9)
        {
            return curMove;//if found in current rule, then update
        }

        if (rule < 9) // if not the last rule (9), then search the subtree
        {
            tempMove = getMinMoves(curClocks, rule + 1, curMove);
            if ((tempMove > 0) && (tempMove < minMove))
            {
                minMove = tempMove;
            }
        }
    }

    //if not results from the current search, then check the result from subtree
    if (rule == 9)
    {
        return 0;// last rule, so nothing found
    }
    else{
        if (minMove == (1 << 29))
        {
            return 0;// keep unchanged, nothing found
        }
        else{
            return minMove;
        }
    }
};

void print(FILE* fout, int minMove)
{
    int i = 0;
    int j = 1;
    int found = 0;
    do
    {
        if (j & minMove)
        {
            if (found)
            {
                fprintf(fout, " ");
            }
            fprintf(fout, "%d", i / 3 + 1);
            found = 1;
        }
        j = j << 1;
        i ++;
    } while (i < 27);
    fprintf(fout, "/n");
}

这个算法从第一种移动方法开始遍历至第9种移动方法以满足要求数字最小。唯一的剪枝发生在黑体字处,如果在遍历第n种做法的时候找到了合适的,则立即返回,因为这个必定是最小的。可以这样看,如果当前找到的数字是“xxxnn”,那么 首先可以排除有“xxx(n-1)”这样的数字,然后有的必然是“xxx(n+1)……”这样的字符串 ,假设现在需要k个n(1<=k<=3),那么要用其它的移动方法来代替k个n,其数量>=k。唯一相等的情况发生在某个移动方法和第n种方法完全一致的情况下。由于我是从第1种移动方法遍历到第9种,这样的话那个和第n种 移动方法一致的移动方法的序号一定>n,所生成的数字一定大,故剪枝。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值