USACO:1.4.2 The Clocks 时钟(IOI 94) 解析

USACO:1.4.2 The Clocks 时钟(IOI 94) 解析

IOI'94 - Day 2

题目描述:

考虑将如此安排在一个3X3 行列中的九个时钟:

|-------|    |-------|    |-------|    
|       |    |       |    |   |   |    
|---O   |    |---O   |    |   O   |          
|       |    |       |    |       |           
|-------|    |-------|    |-------|    
    A            B            C

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

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

目标要找一个最小的移动顺序次将所有的指针指向12 点。

       下面原表格列出了9 种不同的旋转指针的方法,每一种方法都叫一次移动。选择1 到9 号移动方法,将会使在表格中对应的时钟的指针顺时针旋转90 度。

移动方法 受影响的时钟

MoveAffected clocks
1ABDE
2ABC
3BCEF
4ADG
5BDEFH
6CFI
7DEGH
8GHI
9EFHI
例如:

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 
[但这可能不是正确的方法,请看下面]
PROGRAM NAME:clocks
INPUT FORMAT:
第1-3 行: 三个空格分开的数字,每个数字表示一个时钟的初始时间,3,6,9,12。数字的含意和上面第一个例子一样。
SAMPLE INPUT (file clocks.in)
9 9 12
6 6 6
6 3 6
OUTPUT FORMAT:
单独的一行包括一个用空格分开的将所有指针指向12:00 的最短移动顺序的列表。如果有多种方案,输出那种使的连接起来数字最小的方案。(举例来说5 2 4 6 < 9 3 1 1)。
SAMPLE OUTPUT (file clocks.out)

4 5 8 9

解题思路:

       首先得承认,这个简单的题我也没做出来,辜负了大家的期待。。。但)必须向前看:敢于面对挫折和失败,不断尝试,积累知识,厚积薄发!

在这里,我解读一下USACO的源码思路:

       做题之前,我们先看清题意,做简单的分析:我们可以发现两点,1、每个时钟是有周期的,最多移动3次(因为钟指针的周期性);2、输出格式中明确要求,“最短移动顺序”和“连接起来数字最小”,即为移动次数最小且移动方法的(可重复)字典序最小。基于此,我们可以设计一个1-9(方法)的递归,同时每第n次递归中尝试rep重复 3到0次方法n,先rep 3次保证字典序最小(次数相等时)。

/* Notice that the order in which we apply moves is irrelevant, and that applying a move four times is the same as applying it not at all.Thus there are only 49 = 262144 move sequences that need to be tried, so we might as well just try them all.We don't generate them shortest first,but looking at sequences of the same length, we generate the lesser ones before the greater ones, so we only need to keep track of the shortest working sequence we've found.*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>

#define INF 60000	// bigger than longest possible path ???

char  * movestr[] = { "abde", "abc", "bcef", "adg", "bdefh", "cfi", "degh",//???
                    "ghi", "efhi" };//表中内容

int movedist[9][9];//表对应矩阵;
int clock[9];

int bestmove[9];//flag 标志表中1-9操作的次数;
int nbestmove;

/* translate move strings into array "movedist" of which clocks change on each move */
void
mkmove(void)
{
    int i;
    char *p;

    for(i=0; i<9; i++)
	for(p=movestr[i]; *p; p++)
	    movedist[i][*p-'a'] = 3;//翻译成9x9的
}

/* apply some number of each move from k to 9 ,
move contains the number of times each move is applied */
//move中临时存数据move[i]为表中第i操作的次数
void
solve(int *move, int k)
{
    int i, j, n, rep;

    if(k == 9) {       //???
	for(j=0; j<9; j++)
	    if(clock[j]%12 != 0)
		return;

	/* we have a successful sequence of moves */
	n = 0;
	for(j=0; j<9; j++)
	    n += move[j];

	if(nbestmove == 0 || n < nbestmove) {
	    nbestmove = n;
	    for(j=0; j<9; j++)
		bestmove[j] = move[j];
	}
	return;
    }


/* the for loop counts down so we generate smaller numbers first by
trying more of small numbered moves before we try less of them.???
     */
    for(rep=3; rep>=0; rep--) {
	/* apply move k rep times *///每个最多移动4次
	for(i=0; i<rep; i++)
	    for(j=0; j<9; j++)
		clock[j] += movedist[k][j];//完成第k种移动

	move[k] = rep;//

	solve(move, k+1);

	/* undo move */ //恢复移动前状态
	for(i=0; i<rep; i++)
	    for(j=0; j<9; j++)
		clock[j] -= movedist[k][j];
    }
}



void
main(void)
{
    FILE *fin, *fout;
    int i, j, move[9];
    char *sep;// 字符指针 c-串

    fin = fopen("clocks.in", "r");
    fout = fopen("clocks.out", "w");
    assert(fin != NULL && fout != NULL);

    mkmove();//翻译表 

    for(i=0; i<9; i++)
	fscanf(fin, "%d", &clock[i]);

    solve(move, 0);

    sep = "";//
    for(i=0; i<9; i++) {     //???
	for(j=0; j<bestmove[i]; j++) {
	    fprintf(fout, "%s%d", sep, i+1);
		sep = " ";
	}
    }
    fprintf(fout, "\n");

    exit(0);
}

         原码引自USACO,请原谅我造次引用和解读你们的代码!

       由于自身是初学者,编程能力有限,未达到程序员的水平原因,可能误导大家,请大家甄读;文字编辑一般,文中会有言辞不当。博文中的错误和不足敬请读者批评指正。    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝亦

感谢博主辛勤的付出

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值