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

来源:互联网 发布:澳洲新闻软件下载 编辑:程序博客网 时间:2024/06/06 08:31

T1:

第一题不讲了,小学数学,一年级,没A的自觉吃屎。直接上代码

function pd(x1,y1,z1,x2,y2,z2:longint):longint;begin        if x1*3600+y1*60+z1<=x2*3600+y2*60+z2 then                exit((x2*3600+y2*60+z2)-(x1*3600+y1*60+z1))        else                exit(86400-(x1*3600+y1*60+z1)+(x2*3600+y2*60+z2));end;


procedure zz(x,y,z,t:longint);var        sum,w1,w2,w3:longint;begin        sum:=x*3600+y*60+z+t;        if sum>=86400 then                dec(sum,86400);        w1:=sum div 3600;        dec(sum,w1*3600);        w2:=sum div 60;        dec(sum,w2*60);        if w1<10 then                write('0',w1,':')        else                write(w1,':');        if w2<10 then                write('0',w2,':')        else                write(w2,':');        if sum<10 then                write('0',sum)        else                write(sum);        writeln;end;

T2:

第二题考试的时候 ,读错题目,以为是任意连续3个都不相同,结果当然是爆零。正解目测就知道是DP,因为递归超时的题目,又可以划分阶段,一般都是DP。把前i个数划分阶段。用f[i,j]表示前i个数,当前放在j车站的最小值。(1<=i<=n)(1<=j<=3)然后很明显方程就是其余2个最小的再加上当前a[i,j]

   f[i,j]:=min(f[i,j],f[i,j])+a[i,j]。前面j的值为不等于j的另外2个值。

for i:=1 to n do                for j:=1 to 3 do                        if j=1 then                                f[i,j]:=min(f[i-1,2],f[i-1,3])+a[i,j]                        else                        if j=2 then                                f[i,j]:=min(f[i-1,1],f[i-1,3])+a[i,j]                        else                                f[i,j]:=min(f[i-1,1],f[i-1,2])+a[i,j];
T3:

这道题是4题中最难的。很明显可以发现,只要判断一下回路和没有回路的路径即可。例如:
例如这个图,很明显可以发现3,4,5构成了环路,可是怎么在接近线性的时间来判断环路呢?

说到这个,我们很容易想到了拓扑排序,把没有爸爸的节点给删掉,于是,我们就可以用O(2n)的方法,一个枚举哪个没有爸爸,一个对于没有爸爸的这个点按照它对应到达的城市进行删除。删除最多为n,枚举也是n,所以为O(2n)

用这个例子来说,一开始扫到了没有爸爸的1,按照顺序,删掉1,然后把c[1](表示1这个位置爸爸节点的个数)赋值为-1,然后它对应c[b[i]]也就是对应的城市-1.然后到2继续扫,我这里是扫到了1个就对他进行循环+删除。

很明显,最后c[i]的值大于0的,一定会是回路,因为回路互为父亲。

for i:=1 to n do                if c[i]=0 then                begin                        k:=i;                        while c[k]=0 do//如果当前的值没有父亲                        begin                                c[k]:=-1;//把他赋值为-1,意思为不是回路                                dec(c[b[k]]);//对应那个城市他父亲个数的值减一                                k:=b[k];//继续判断,将当前指针指向对应城市                        end;                end;
然后我们就可以判断他的回路了。很明显,按照他的走。如果碰到已经碰到过的路,则把整个走过的回路赋值为它刚才一共走的值,这样在第二次循环碰到是这个回路中的路就不用再次模拟了。

fillchar(h,sizeof(h),255);//h[i]表示第i个位置是回路的值。因为输入的值可能为0,所以全部为-1(fillchar255就是1)        for i:=1 to n do                if (c[i]>=1)and(h[i]=-1) then//判断当前是否为回路并且当前是否判断过回路                begin                        k:=i;                        ans:=0;                        while (k<>i)or(ans=0) do                        begin                                inc(ans,a[k]);                                if k=b[k] then break;//注意有坑爹数据,加上这个判断,如果自己和自己构成回路就直接退出,否则会一直循环。                                k:=b[k];                        end;//这个就是按照回路来走,并且累加它的和                        k:=i;                        while  (k<>i)or(h[k]=-1) do                        begin                                h[k]:=ans;                                k:=b[k];                        end;//把刚才搞过的回路都赋值为ans                end;
最后一个步骤就是输出了。输出的时候,如果当前为回路,就直接输出,否则就要计算了。从当前点走,直到碰到一个回路,就输出原先当前点到第一个回路的值再加上这个回路的数值即可。有个优化,懒得加了。例如有1,2,3。3这个位置是回路,那么你算了1->3的路径,倒过来,就像递归一样(当然可以用递归实现)路径压缩,返回2->3的值了。请选手自行修改

for i:=1 to n do                if h[i]>0 then                        writeln(h[i])                else                begin                        ans:=a[i];                        k:=b[i];                        while (h[k]=-1) do                        begin                                inc(ans,a[k]);                                k:=b[k];                        end;                        writeln(ans+h[k]);                end;
T4:

bfs,利用了最先找到的值一定为最优的特点,注意细节,太水了,做过产生数的都会做。上代码。

第一种情况:

for i:=1 to length(s)-1 do                        for j:=i+1 to length(s) do                        begin                                str(now,ss);                                交换两个位置的值                                val(ss,p);                                if (bz[p]=0)and(p<>n) then                                begin                                        tail:=tail mod 100000+1;                                        d[tail,0]:=d[head,0]+1;                                        d[tail,1]:=p;                                        bz[p]:=d[tail,0];                                end;//判断入队                        end;
第二种情况:

for i:=1 to length(s) do                begin                        str(now,ss);                        delete(ss,i,1);                        val(ss,p);                        if (bz[p]=0)and(p<>n) then                        begin                                tail:=tail mod 100000+1;                                d[tail,0]:=d[head,0]+1;                                d[tail,1]:=p;                                bz[p]:=d[tail,0];                        end;                end;
第三种情况:

for i:=1 to length(s)-1 do                        for k:='1' to '9' do                        begin                                ss:=s;                                if (ss[i]<k)and(k<ss[i+1]) then                                begin                                        str(now,ss);                                        insert(k,ss,i+1);                                        val(ss,p);                                        if (bz[p]=0)and(p<>n)and(length(ss)<=length(jj)) then                                        begin                                                tail:=tail mod 100000+1;                                                d[tail,0]:=d[head,0]+1;                                                d[tail,1]:=p;                                                bz[p]:=d[tail,0];                                        end;                                end;                        end;
代码码的有点丑,有点麻烦,比赛赶时间,随便打了一下,请自行更改。

总结:

码的智障,有人用百度优先搜索。第一题水过,第二题看错题目,第三题没想到,不过想到拓扑,但是想到超时就止步了。最后一道题粗心码错了

估分:100+100+0+100

得分:100+0+0+40

Orz接近最后。下次加油

0 0
原创粉丝点击