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

来源:互联网 发布:qq.com 域名 编辑:程序博客网 时间:2024/05/22 10:35

题目网址:http://blog.csdn.net/fengyingjie2/article/details/51900664

总结:这次比赛考的时候觉得很难很难,改的时候觉得很水很水。觉得考试的时候很多大胆的想法都没有实现,例如第三题,随随便便打了个递归就了事了。总体来说题目不是很难,水平有待提高,敲键盘也要敲多点了/(ㄒoㄒ)/~~我还懂一个道理:暴力出奇迹,水法真神奇!

T1:

       第一题我刚看题目,怎么这么水!可是这是提高组的题目。我又看了一遍,n≤10,才发现TG原来有水题,刚开始以为要DP呢~回溯AC!!!O(∩_∩)O~~上代码:

<span style="font-size:14px;">var        n,min:int64;        i:longint;        a,b:array[0..10] of longint;procedure dg(k,ans,numa,numb:int64);//参数代表:枚举到第几个,一共用了多少原料,酸度,和苦度begin        if (abs(numa-numb)<min)and(ans>=1) then                min:=abs(numa-numb);        if k>n then exit;        dg(k+1,ans+1,numa*a[k],numb+b[k]);//选        dg(k+1,ans,numa,numb);//不选end;begin        readln(n);        min:=maxlongint;        for i:=1 to n do                readln(a[i],b[i]);        dg(1,0,1,0);        writeln(min);end.</span>
T2:

     这道题比赛的时候看起来是DP,然而怎么也推不出方程。于是用贪心打了一遍,居然0分!!!如果做过usaco里面A GAME这道题的人99.99%会做这道题。

      详解:省略……做出来补充

代码:没做出来……

T3:

     第三题刚看题目,其实很熟悉,小学OJ有个同样的题目叫:数列,也是删数的。比赛的时候,其实我想的方法就是正解:把一个数列有的,然而别的数列没有的,这一列,删掉!而且还要重头开始循环删(因为你删掉了某一列之后,可能这3列共同有的这个数你删掉了,导致这一列缺这个数,引起错误),所以,你要删N多次,直到上一次没有删为止。我考试打了个递归,不会实现之前我想的算法,于是10分。。代码:

<span style="font-size:14px;">var        i,j,k,n,m,x,y,p,q,ans:longint;        a,b,c:array[1..100000]of longint;        bz3:boolean;        bz,bz1,bz2:array[1..100000]of longint;begin        readln(n);        for i:=1 to n do        begin                read(a[i]);                bz[a[i]]:=1;        end;        for i:=1 to n do        begin                read(b[i]);                inc(bz1[b[i]]);        end;        for i:=1 to n do        begin                read(c[i]);                inc(bz2[c[i]]);        end;        bz3:=true;        while bz3 do//上一次没删过就退出        begin                bz3:=false;                for i:=1 to n do                begin                        if (bz[a[i]]>0) and ((bz1[a[i]]=0)or (bz2[a[i]]=0)) then//第一列有的别的没有的情况下                        begin                                dec(bz[a[i]]);                                dec(bz1[b[i]]);                                dec(bz2[c[i]]);//删掉这一列的数                                bz3:=true;//表示删过了,                                inc(ans);                        end;                end;        end;        write(ans);end.//这个是同学的代码@hgr,我的代码打得有点猥琐,不好说</span>
T4:

    考试的时候没看懂。想不到这道题如此简单。意思就是:一个最大的区间包住一个次大的,次大的包住次次大的……求最多能包住多少个区间。

    很明显的知道,先排个序,把a[i,1]当做排序的第一元素(第一关键字),然后a[i,2]就是第二个。如果在a[i,1]相等的前提下,a[i,2]较大的优先。a[i,1]从小到大排序。然后我们就做一个最长不上升子序列,来把区间给串起来(包住)。题目N≤100000,所以,n²一定会超时。我们就可以优化一下最长不上升子序列,怎么优化,敬请收看下一段。

    最长不上升子序列,就是多了一个F数组,这个F[i]表示第i长度结尾的最小值!把过程说出来可能也听不懂(语文差),还是出个数据来说说吧!

数列:

1 7

1 6

1 5

1 4

2 5

2 5

2 5

排序完变成:

1 7

1 6

1 5

1 4

2 5

2 5

2 5

然后,第一列没用了。第二列为7.6,5,4,5,5,5。一开始,固定把第一个,就是7丢进f数组里面去。f[1]:=7;然后从f数组的每个数倒过来跟后一个比较。6≤7,就把6丢进去,f[2]:=6,5≤f[2],丢进去,f[3]:=5,4≤f[3],f[4]:=4。这时候,问题来了,也是我一开始错的地方。5比f[4]大,我们就不做判断,来到和f[3]比较,发现符合条件5<=f[3],所以我们把f[3+1]的4替换成5.注意:如果F数组最后一个元素不符合当前枚举的a[i,2]数组的条件,就是a[i,2]>f[len],那么我们就要继续从f[len-1~~1]这么范围去枚举,找到了符合条件的,就把符合条件的后一个数,就是f[符合条件为止+1]变成a[i,2]。如果这个数比整个数列都大,就把他跟第一个换。以此类推,f数组为[7,6,5,5,5,5],长度为6,最长不上升子序列就是6了!!效率为O(n logn),够快吧!于是AC,代码:

var        n,i,j,sum,min:longint;        a:array[0..100000,0..2] of longint;        f:array[0..100000] of longint;        bz:boolean;procedure q(l,r:longint);var        i,j,m1,m2,x:longint;begin        i:=l;        j:=r;        m1:=a[(l+r) shr 1,1];        m2:=a[(l+r) shr 1,2];        repeat                while (a[i,1]<m1)or((a[i,1]=m1)and(a[i,2]>m2)) do inc(i);                while (a[j,1]>m1)or((a[j,1]=m1)and(a[j,2]<m2)) do dec(j);                if i<=j then                begin                        x:=a[i,1];                        a[i,1]:=a[j,1];                        a[j,1]:=x;                        x:=a[i,2];                        a[i,2]:=a[j,2];                        a[j,2]:=x;                        inc(i);                        dec(j);                end;        until i>j;        if l<j then q(l,j);        if i<r then q(i,r);end;//快排begin        readln(n);        for i:=1 to n do                readln(a[i,1],a[i,2]);        q(1,n);        f[1]:=a[1,2];//一开始f第一个为a[1,2]        sum:=1;        for i:=2 to n do        begin                bz:=false;                for j:=sum downto 1 do                        if f[j]>=a[i,2] then//符合条件了                                if sum<>j then//但是并不是f[len]能符合条件的                                begin                                        f[j+1]:=a[i,2];//换掉后一个                                        bz:=true;                                        break;//直接break,加快速度                                end                                else                                begin                                        inc(sum);//长度+1                                        f[sum]:=a[i,2];//如果最后一个就符合条件的,就长度+1,把新的a[i,2]丢进去                                        bz:=true;                                        break;                                end;                if not bz then//如果整个F数组都没a[i,2]大                        f[1]:=a[i,2];第一个就换成a[i,2]        end;        writeln(sum);end.
0 0
原创粉丝点击