ACM解题总结——HihoCoder1153
2016-02-20 14:44
351 查看
题目来源:
HihoCoder 1153
题目要求:
以下是常见的手机数字键盘的排列方式:
1 2 3
4 5 6
7 8 9
0
开始状态下,你的手指的在按键1的位置。可以执行的操作有:按下手指所在位置的按键,或者将手指向下移动到下一个按键,或者将手指一定到右边的下一个按键。而向左和向上移动是不允许的。
要求给定一个数字K,在不限制操作次数的情况下,求出在这样的操作方式下,可以输出的不大于K的最大数字。
解答:
这个题目比较简单。数据量比较小,所以用最朴素的深度优先搜索算法就可以得到结果。
首先需要了解的是,当手指处于某一个位置时,它还可以移动到那些位置。这里可以利用可达矩阵得到这些信息。首先将题目中的键盘转化成一个有向图,如下:
![](https://img-blog.csdn.net/20160304134025250?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
然后,就可以得到可达矩阵,如果从数字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可达的所有数字,依次代入,看是否有解,找到是的结果最大的解即可。伪代码如下:
由于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。
伪代码如下:
最后搜索完毕后,输出result中存储的内容即可。
输入输出格式:
输入:输入的第一行为一个数字T,代表有T组测试数据;接下来的T行,每一行为一个数字K。
输出:对于每一组测试数据,输出一个值,代表可以得到的不大于K的最大值。
数据范围:
1≤K≤10^500
1≤T≤20
程序代码:
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; }
相关文章推荐
- 简单的四则运算
- 数的奇偶性
- ACM网址
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- 矩阵的乘法操作
- 蚂蚁爬行问题
- 蚂蚁爬行问题
- 求两个数的最大公约数【ACM基础题】
- 打印出二进制中所有1的位置
- 杭电题目---一只小蜜蜂
- HDOJ 1002 A + B Problem II (Big Numbers Addition)
- 初学ACM - 半数集(Half Set)问题 NOJ 1010 / FOJ 1207
- 初学ACM - 组合数学基础题目PKU 1833
- POJ ACM 1002
- POJ 2635 The Embarrassed Cryptographe
- POJ 3292 Semi-prime H-numbers
- POJ 2773 HAPPY 2006
- POJ 3090 Visible Lattice Points