ACM解题总结——HihoCoder1153

题目来源:
        HihoCoder 1153

题目要求:
    以下是常见的手机数字键盘的排列方式:
                    1 2 3 
                    4 5 6
                    7 8 9
                      0
    开始状态下,你的手指的在按键1的位置。可以执行的操作有:按下手指所在位置的按键,或者将手指向下移动到下一个按键,或者将手指一定到右边的下一个按键。而向左和向上移动是不允许的。
    要求给定一个数字K,在不限制操作次数的情况下,求出在这样的操作方式下,可以输出的不大于K的最大数字。

解答:
    这个题目比较简单。数据量比较小,所以用最朴素的深度优先搜索算法就可以得到结果。
    首先需要了解的是,当手指处于某一个位置时,它还可以移动到那些位置。这里可以利用可达矩阵得到这些信息。首先将题目中的键盘转化成一个有向图,如下:
     
    然后,就可以得到可达矩阵,如果从数字i可以到达数字j,那么矩阵中第i行,第j列数字为1,否则为0,可达矩阵(记为V)如下, 其中行、列的编号都是从0开始的:
1 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
1 0 1 1 0 1 1 0 1 1
0 0 0 1 0 0 1 0 0 1
1 0 0 0 1 1 1 1 1 1
1 0 0 0 0 1 1 0 1 1
0 0 0 0 0 0 1 0 0 1
1 0 0 0 0 0 0 1 1 1
1 0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 0 0  1
    然后,就可以进行搜索求解了。保证得到的结果最大,那么就要尽可能地使结果中的各个数位的数字和K接近。搜索从最高位开始。起始位置是1,由于1可以到达所有数字,因此结果的最高位数字就是K的最高位数字。
    在后续的搜索中,则需要枚举,设上一轮的搜索结果是s, 那么在本轮搜索过程中,参考矩阵V,枚举S可达的所有数字,依次代入,看是否有解,找到是的结果最大的解即可。伪代码如下:
            
boolean search(char* num, int depth, int last) {
                for i = 9 →0 {
                    if(V[last][i] = 1 ) {
                        if(search(num, depth+1, i) {
                            return true;
                        }
                    }
                }
            }


    由于i是倒序枚举的,因此,第一个找到的合法的结果一定是最大的结果。
    但是,上面的代码还有一个问题,就是没有考虑到“不大于K”这个条件,并不是每一轮的递归都可以从9开始枚举,这里设定一个标记tag,记录在进行当前搜索前,前面的搜索的结果是否和数字K中的对应位置相同,如果相同,tag为0, 否则:tag为1。例如对于数字65435,当进行到第3轮递归时,如果结果的前两位也是65,此时tag为1,那么为了保证结果不大于K,第三轮的搜索只能从K的第三位数字4开始,依次枚举:3,2,1,0;如果前两轮的结果是64,此时tag是1,那么由于高位已经有数位小于K,因此无论枚举的数字是多少,结果都不会大于K,此时可以枚举0-9的所有数字。
    因此,修改代码如下,在搜索前,先检查标识tag的值,如果是0,就从K值的对应数位的数字开始搜索,如果是1,那么就从9开始搜索。而在搜索过程中,如果发现搜索的值小于K对应数位的值依然找不到合法结果,那么将tag置为1。例如某个数字的前几位是654...,当在第三轮的递归时发现第三位取4不可以得到合法的结果,那么这说明第三位只能取比4小的数字,也就说明,后面的数位可以取0-9的任意值,因此将tag置为1。
    伪代码如下:
         
boolean search(char* num, int depth, int last) {
               if(tag = 0) {
                    start = num[depth];
                } else {
                    start = 9;
                } 
               for i = start →0 {
                    if(i < num[depth]) {
                        tag = 1;
                    }
                    if(V[last][i] = 1 ) {
                        if(search(num, depth+1, i) {
                            result[depth] = i;
                            return true;
                        }
                    }
               }
          }

最后搜索完毕后,输出result中存储的内容即可。

输入输出格式:
    输入:输入的第一行为一个数字T,代表有T组测试数据;接下来的T行,每一行为一个数字K。
    输出:对于每一组测试数据,输出一个值,代表可以得到的不大于K的最大值。

数据范围:
    1≤K≤10^500
    1≤T≤20

程序代码:

/****************************************************/
/* File        : hiho_week_85.cpp                   */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-02-20                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/

/*
 * Update log:
 *		Create by Zhang Yufei in 2016-02-20.
 *			Submit: AC.
 */
 
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

/*
 * The visitable matrix. If there exist path from number i to j,
 * the value is 1, or 0.
 */
int matrix[10][10] = {
	{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
	{1, 0, 1, 1, 0, 1, 1, 0, 1, 1},
	{0, 0, 0, 1, 0, 0, 1, 0, 0, 1},
	{1, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{1, 0, 0, 0, 0, 1, 1, 0, 1, 1},
	{0, 0, 0, 0, 0, 0, 1, 0, 0, 1},
	{1, 0, 0, 0, 0, 0, 0, 1, 1, 1},
	{1, 0, 0, 0, 0, 0, 0, 0, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
};

/*
 * The input number.
 */
char K[550];

/*
 * The length of input K.
 */
int length;

/*
 * This is a tag, indicating if program can iterate from 9.
 * If tag equals 1, program iterates starting from 9, or from num[depth].
 */
int tag;

/*
 * Store the result to output.
 */
char result[550];

/*
 * This function deals with one input case.
 * Parameters:
 *		None.
 * Returns:
 *		None.
 */
void function(void);

/*
 * This function search the answer for one test case,
 * using iterating method.
 * Parameters:
 *		@num: The number in input.
 *		@depth: The depth of iterate, it also means the position of the number
 *				to deal with.
 *		@last: The last number deal with in the last iterating.
 * Returns:
 *		If program finds an answer, return 1, or 0.
 */
int search(char* num, int depth, int last);

/*
 * The main program.
 */
int main(void) {
	int T;
	scanf("%d", &T);
	for(int i = 0; i < T; i++)
		function();
		
	return 0;	
}

/*
 * This function deals with one input case.
 * Parameters:
 *		None.
 * Returns:
 *		None.
 */
void function(void) {

	scanf("%s", K);
	length = strlen(K);
	tag = 0;
	search(K, 0, 1);
	
	printf("%s\n", result);
}

/*
 * This function search the answer for one test case,
 * using iterating method.
 * Parameters:
 *		@num: The number in input.
 *		@depth: The depth of iterate, it also means the position of the number
 *				to deal with.
 *		@last: The last number deal with in the last iterating.
 * Returns:
 *		If program finds an answer, return 1, or 0.
 */
int search(char *num, int depth, int last) {
	if(depth == length) {
		result[depth] = '\0';
		return 1;
	}
	
	int start;
	if(tag == 0) {
		start = num[depth] - 48;
	} else {
		start = 9;
	}
	for(int i = start; i >= 0; i--) {
		if(matrix[last][i]) {
			if(i < num[depth] - 48) {
				tag = 1;
			}
			if(search(K, depth + 1, i)) {
				result[depth] = i + 48;
				return 1;
			}
		}
	}
	
	return 0;
}
 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值