题目来源:
HihoCoder 1153
题目要求:
以下是常见的手机数字键盘的排列方式:
1 2 3
4 5 6
7 8 9
0
开始状态下,你的手指的在按键1的位置。可以执行的操作有:按下手指所在位置的按键,或者将手指向下移动到下一个按键,或者将手指一定到右边的下一个按键。而向左和向上移动是不允许的。
要求给定一个数字K,在不限制操作次数的情况下,求出在这样的操作方式下,可以输出的不大于K的最大数字。
解答:
这个题目比较简单。数据量比较小,所以用最朴素的深度优先搜索算法就可以得到结果。
首先需要了解的是,当手指处于某一个位置时,它还可以移动到那些位置。这里可以利用可达矩阵得到这些信息。首先将题目中的键盘转化成一个有向图,如下:
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可达的所有数字,依次代入,看是否有解,找到是的结果最大的解即可。伪代码如下:
由于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。
伪代码如下:
在后续的搜索中,则需要枚举,设上一轮的搜索结果是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
输入输出格式:
输入:输入的第一行为一个数字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;
}