高中OJ1302. DigitalCounter

32 篇文章 0 订阅
6 篇文章 0 订阅

题目

我们有一个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就是你要求的数。

解法

明显的数位DP。
(话说可以用贪心)

设F[i,j,0/1]表示当前枚举到第i个数,总电子管剩余j个,是否大于原数的情况下能构出的最小数。

分两种情况讨论:
①k=1 此时我们可以保证当前这个数是大于原数,所以枚举的数没有大小限制,只需要剩下的电子管够。

②k=0 目前枚举到的前i-1位和原数相等,所以枚举第i位时至少要比第i位大。
又分两种情况:
1、枚举的和第i位相等,无法保证大于。
2、比第i位大,可以保证大于。

特殊

根据题目得知,有可能数会到达顶峰时清零。
所以按照上面那样做,就可能得不出答案。

此时就再设g[i,j]表示当前枚举到第i位,剩余电子管数为j。
因为我们已经把大于原数的数都算了一遍,所以我们再算的时候就不能比原数大。

每枚举一位时,当前这一位必须大于等于枚举的数。

源码

const
        b:array[0..9] of longint=(6,2,5,5,4,5,6,3,7,5);
var
        a:array[1..15] of longint;
        f:array[1..16,0..105,0..1] of int64;
        g:array[1..16,0..105] of int64;
        n,i,j,k,l,sum,i2,j2,k2:longint;
        ch:char;
        num,num2:int64;
function min(x,y:int64):int64;
begin
        if x<y then min:=x
        else
        min:=y;
end;
function max(x,y:int64):int64;
begin
        if x>y then max:=x
        else
        max:=y;
end;
begin
        readln(n);
        num2:=1;
        for i:=1 to n do
        begin
                read(ch);
                a[i]:=ord(ch)-48;
                sum:=sum+b[a[i]];
                num:=num*10+a[i];
                num2:=num2*10;
        end;
        readln;

        fillchar(f,sizeof(f),$FF);
        f[1,sum,0]:=0;
        for i:=1 to n do
        begin
                for j:=1 to sum do
                begin
                        for l:=0 to 9 do
                        if j>=b[l] then
                        begin
                                for k:=0 to 1 do
                                if ((k=1) or (l>=a[i])) and (f[i,j,k]>=0) then
                                begin
                                        if l>a[i] then
                                        k2:=1
                                        else
                                        k2:=k;
                                        j2:=j-b[l];
                                        i2:=i+1;
                                        if f[i2,j2,k2]=-1 then
                                        f[i2,j2,k2]:=f[i,j,k]*10+l
                                        else
                                        f[i2,j2,k2]:=min(f[i2,j2,k2],f[i,j,k]*10+l);
                                end;
                        end;
                end;
        end;
        if f[n+1,0,1]>0 then
        begin
                writeln(f[n+1,0,1]-num);
                exit;
        end;

        fillchar(g,sizeof(g),$FF);
        g[1,sum]:=0;
        for i:=1 to n do
        begin
                for j:=1 to sum do
                if g[i,j]>=0 then
                begin
                        for l:=0 to a[i] do
                        if j>=b[l] then
                        begin
                                i2:=i+1;
                                j2:=j-b[l];
                                if g[i2,j2]=-1 then
                                g[i2,j2]:=g[i,j]*10+l
                                else
                                g[i2,j2]:=min(g[i2,j2],g[i,j]*10+l);
                        end;
                end;
        end;
        writeln(num2-num+g[n+1,0]);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值