2016.08.15【初中部 NOIP提高组 】模拟赛C题解

来源:互联网 发布:好的码字软件 编辑:程序博客网 时间:2024/06/05 20:57

这次的比赛实在是太水沝淼㵘了,前两题完全是来送分的(然而我第一题特么0分)


T1

这题是最水也是最容易被坑的(比如我)。。。

Easy的字符串处理,之后模拟加减法就AC了


K=0的情况:

我们先把两个时间比较一下,如果第一个时间比第二个大,就说明已经经过了一天,就要把第二个时间加上24个小时,然后把两个时间相减,就是时减时,分减分,秒减秒,如果减出了负数就向前一位借位,然后再把时*3600+分*60+秒输出就可以了。


K=1的情况:

我们用模拟加法来解决这个问题。首先先把秒加上len,之后如果秒大于等于60则向前进位,之后分也向时进位,如果时大于等于24就直接mod 24就可以了,最后在判断一下,如果输出的某一项不足两位数就在前面输出一个0。


T2

水的不能再水的DP。。。

我们只要先建一个数组a[1..2,1..3],值为(2,3),(1,3),(1,2)表示除了自己所在列外的另外两列。

之后我们在建一个数组f[i,j],表示第i行选择第j种地铁的最低成本。

因为当前列不能和上一列相同,所以得到这样一个状态转移方程:

f[i,j]=min(f[i-1,a[j,1]],f[i-1,a[j,2]])+d[i,j](d是输入的数据)


T3

这道题我们可以先用拓扑排序先删除掉没有入度的节点,剩下的都是已经成环的。

然后计算每一个环的结果,把每一个环中的所有节点都更改为这个结果。

再计算那些没有成环的节点,最后输出答案就帅(wei)气(suo)的AC了


提示:有一些坑(ri)爹(gou)的数据,a[i]=0且f[i]=i,这样会造成死循环,例如这组数据:

2

1 0

2 2


如果这组数据卡住了就说明出现了这种情况,我们只需要再判断一下就可以了。


鉴于某些同学的要求,我把代码发上来吧。。。

var
        a:array[1..200000] of longint;
        f:array[1..200000] of longint;
        b:array[1..200000] of longint;
        bz:array[1..200000]of boolean;
        c:array[1..200000] of longint;
        n,i,j,k,l:longint;
        pd:boolean;
begin
        readln(n);

        for i:=1 to n do
        read(a[i]);

        for i:=1 to n do
        begin
                read(f[i]);

                inc(c[f[i]]);
        end;

        pd:=true;

        while pd=true do
        begin
                pd:=false;

                for i:=1 to n do
                if c[i]=0 then
                begin
                        c[i]:=9999999;

                        dec(c[f[i]]);

                        pd:=true;
                end;
        end;

        for i:=1 to n do
        if (bz[i]=false) and (c[i]<999999) then
        begin
                j:=f[i];

                bz[i]:=true;

                k:=a[i];

                while bz[j]=false do
                begin
                        bz[j]:=true;

                        k:=k+a[j];

                        j:=f[j];
                end;

                j:=f[i];

                b[i]:=k;

                while j<>i do
                begin
                        b[j]:=k;

                        j:=f[j];
                end;
        end;

        for i:=1 to n do
        if b[i]=0 then
        begin
                j:=i;

                while (b[j]=0) and not((j=f[j]) and (a[j]=0)) do
                begin
                        b[i]:=b[i]+a[j];

                        j:=f[j];
                end;

                if i<>j then
                b[i]:=b[i]+b[j];
        end;

        for i:=1 to n do
        writeln(b[i]);
end. 


仅供参考,谢绝抄袭


T4

这道题直接暴力BFS就可以AC了。。。

我们定一个循环队列D,然后把d[1]设为输入的n。

再定一个B数组,b[i]表示从状态n经过操作后变成状态i所需的最小步数。

先把b数组初始化,再把b[n]设为0。

之后对于每种情况进行三种操作。

第一种:

我们只要二重循环枚举交换的位置,之后把两个数字交换。

第二种:

先判断一下当前字符串的长度是否大于1,然后在枚举删除的数的位置。

第三种:

先判断当前字符串长度是否小于初始长度,然后先把字符串转成数字,再放在数组里。之后枚举i、j,i表示当前从那个位置插入数字,j表示当前插入的数字。i从1~l-1(l表示当前字符串的长度),j从当前位数字+1~下一位数字-1,用insert(chr(j+48),s,i+1)(注意一定是+1,因为要想插入到第一个数的后面就一定要插入到第二个位置)插入字符。


每次操作时都先把原数转成字符串,操作完后再转回数字。


最后在根据输入输出答案,如果当前的结果为b数组的初始值,就代表不能产生该结果,就直接输出-1。


最后提示:由于s<100000,而且s不能包含0,所以s最多只有100000-1,b数组开到99999就可以了。

真·最后提示:循环队列不要开的太小,要开到一百万左右,不会爆空间。


看在我这么辛苦打字的份上,顶一个吧!


1 0