3600: 计数-训练套题T2T3
Description
3.计数 [DigitalCounter.pas/ c/cpp]
【问题描述】
我们有一个N位数字的电子表,当时间到达10^N-1时,下一秒就归0。下面我们给出数字0 到 9的模拟图。
+ +---+ +---+ + + +---+
| | | | | |
+ +---+ +---+ +---+ +---+
| | | | |
+ +---+ +---+ + +---+
+---+ +---+ +---+ +---+ +---+
| | | | | | | |
+---+ + +---+ +---+ + +
| | | | | | | |
+---+ + +---+ + +---+
对于每个数字,相邻两个+之间会有一根电子管,当显示该数字时,这些电子管就会发亮。如上图所示:数字0到9,它们的电子管数量分别是:6、2、 5、 5、 4、 5、 6、 3、 7、 5。
设现在的时刻是X, 那么可以算出该时有多少根电子管是亮的。比如:现在时刻是:99,那么共有5 + 5= 10根电子管是亮的。假如从现在时刻开始,再过Y秒后,时刻显示为Z, 我们的问题是:求最小的Y,使得时刻Z发亮的电子管数量与时刻X发亮的电子管数量相等。如:现在X = 99 ,那么再过Y = 5 秒后, 时刻变成了Z = 04, 而时刻Z发亮的电子管数量 = 6 + 4 = 10。于是Y = 5就是你要求的数。
【输入数据】
第一行:一个整数N,表示电子表是10^N进制的。1 <= N <= 15。
对于30%数据,N < 7.
第二行:一个整数X, 表示现在的时刻,可能有前导0。X有N位数字。
【输出数据】
一行:最小的整数Y, 表示从现在X时刻开始,再过Y秒,得到的时刻Z发亮的电子管数量与时刻X发亮的电子管数量相等。
【输入样例】
3
007
【输出样例】
11
【样例说明】
因为数字007有6+6+3 =15根电子管发亮,所以过11秒后,电子表显示数字018时,才能满足发亮的电子管数量相等。018时刻发亮的电子管数量 = 6 + 2 + 7 = 15
HINT
计数 DigitalCounter
n 数字0到9,它们的电子管数量分别是:6、2、 5、 5、 4、 5、 6、 3、 7、 5。
n 设现在的时刻是X, 那么可以算出该时有多少根电子管是亮的。
n 假如从现在时刻开始,再过Y秒后,时刻显示为Z, 我们的问题是:求最小的Y,使得时刻Z发亮的电子管数量与时刻X发亮的电子管数量相等。
n 统计一个数X有多少根电子管是亮的很容易
n 如果每次1秒增加,模拟现实过程,则因为最多有15位数,显然要超时。(1 <= N <= 15)
n
怎样做呢?
二分法?
找规律?
递推或DP?
n 没有单调性,不能二分。
n 找规律也好像不可能。
n
递推之类的?
不能按时间,那样会超时
按什么呢?
n
题目中的其它可能“变量”:
1)数X的位数len---15位
2)亮的电子管个数light---也不大<=15*7
n 设函数f(a)表示数a的有多少电子管是亮的。
n 初始数是X,则要找Y,使f(Y)=f(X)
n
假设Y>X,则Y尽量小:
1)开头数dig尽量小
3)开头数也一样?比较第二位数
…..
n 问题与长度、开头数、电子管数的判断有关。
n 工具函数g( len, light) 判断问题。(没dig?)
n g( len, light) 函数推算:
n g(len, light) = or g(len-1,light-f(i) ) i=0~9
即开头数字是0~9中任何一个成立都可以。
n 显然个g数组比较容易DP出来。
n g( len, light) 函数推算:
n g[0,0]:=true;
n for i:=1 to N do
n for j:=1 to 120 do
n for k:=0 to 9 do
n g[ i,j ] := g[i,j] or g[i-1, j - c[k] ];
n 有了g( len, light) 要求最接近X的Y
n 先看Y>X的情况:
n 考察例子:X =98675 f(x) = 26
1)先看最后一位:
y = 98676~98679 看有没有f(y)=26的
2)再看第二位:
y =9868*~9869* 如果有f(y)=26,再定*
3)同理看 y=987**~989**
n 即先从低位到高位,从小到大,先看能否有一个类似: 988**成立,再推算最小的数**。
n
什样推算?
例如: 988**中,如果**还要7个电子管发亮,则只要从高位到低位试,看:0*~9*那些可行,比如1*可行,则看*中电子管为7-f(1)=7-2=5的,即0~9中选最小的电子管是5的数字2即可。最后答案为98812
n Y<=X的情况只要推算最小的*****即可。
n function getBig( var len, dig :integer) :boolean;
n begin
n fx:=0;
n for i:=1 to N do //从低位到高位
n begin
n fx := fx + c[ d[ i ] ]; //原X的前i位电子管数
n for j:=d[ i ]+1 to 9 do //比原X第i位大的数,从小到大
n if (fx>=c[j]) and (g[ i-1, fx - c[ j ] ] ) then
n begin len:=i; dig:=j; exit( true); //找到最小的
n end;
n end;
n exit (false);
n end;
n procedure getMin(len, fx:integer);
n begin
n for i:=len downto 1 do //从高位到低位
n begin
n for j:=0 to 9 do //尽量小数字
n if (fx>=c[j]) and (g[ i -1, fx - c[j] ] ) then
n break;
n ans[ i ] := j;
n fx := fx-c[j]; //后面还有多少电子管
n end;
n end;
procedure calc();
var len, dig :integer;
begin
ans:=d;
if getBig(len,dig) then
begin //如果在>X的Y,即不“环绕”
ans[len]:=dig;
getMin(len-1, fx-c[dig]);
yy:=0;
for i:=N downto 1 do yy := yy*10 + ans[i];
Y:= yy - X;
end
else begin //如果“环绕”了
getMin(N, fx); //求一个尽量小的
yy:=0; Y:=1;
for i:=N downto 1 do begin
yy := yy*10 + ans[i];
Y := Y*10; //求10^N
end;
Y:= Y-X + yy;
end;
end;