2016.08.11【初中部 NOIP提高组 】模拟赛C

一直在改题,很多题解都没写了~刚好有点空,就短短滴总结一下昨天的比赛吧(ノ=Д=)ノ┻━┻


第一题:

https://61.142.113.109/senior/#main/show/2540

题目描述:

  辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”    如果你是辰辰,你能完成这个任务吗?

Input

  输入文件的第一行包含两个正整数N,M。M表示总共能够用来采药的时间,N代表山洞里的草药的数目。接下来的N行每行包括两个的整数,分别表示采摘某株草药的时间Ti和这株草药的价值Vi。

Output

  输出文件仅包含一个整数表示规定时间内可以采到的草药的最大总价值。

Sample Input

3 9 10 10 8 1 1 2

Sample Output

3

Data Constraint

50%的数据中 N,M ≤ 1000; 100%的数据中 N,M ≤ 100000,Ti,Vi ≤10。

 



一道01背包问题,但是.................

50%的数据中 N,M ≤ 1000; 100%的数据中 N,M ≤ 100000,Ti,Vi ≤10。

这题数据好大啊~鄙视

于是我比赛时dp然后只有50分~

“什么?难道还有比dp更先进的算法!???!!!”当时我就瞬间蒙了

其实呢,我们看到时间Ti和价值Vi 都≤10,我们就可以从这里下手,把它转化成多重背包问题,但还是超时只有50分;那么我们该怎么办呢?

所以,我们可以优化一下多重背包.

我们可以先用一个二位数组记录每种情况的个数,之后再把每一种背包都拆分成若干份,使得这若干份小背包通过组合,可以组合出1~背包数中的每一种可能(这一段要理解好)

假设一种背包的数量为X
则X可以拆分成2^0+2^1+……+2^log(x)
如果拆分完X还有剩余,则最后一项的值是剩下的数,

之后把这些数进行01背包,就可以算出最后的答案。


好吧这题我还没改出来,就附上个50分的01背包代码吧~

参考代码:

请勿抄袭!请勿抄袭!请勿抄袭!重要的事情说三遍!

var     i,j,t,n:longint;
        f:array[0..3000,0..3000]of longint;
        w,v:array[1..3000]of longint;
function max(a,b:longint):longint;
begin
        if a>b then exit(a) else exit(b);
end;
begin
        read(t,n);
        for i:=1 to t do
                read(w[i],v[i]);
        for i:=1 to t do
                for j:=1 to n do
                        if j>=w[i] then
                        f[i,j]:=max(f[i-1,j],f[i-1,j-w[i]]+v[i]) else
                        f[i,j]:=f[i-1,j];
        writeln(f[t,n]);
end.






第二题:

https://61.142.113.109/senior/#main/show/2541


题目描述:

  给定一个N*M的矩阵,记录左上角为(1,1),右下角为(N,M),现在从(1,1)开始取数,每次只能向下或向右移动一个单位,最终到达(N,M),我们把路径上所有的数相乘,记为C。使C的结果最大已经不能满足我们了,现在我们想让C末尾的零最少。
  Ps.11000末尾有3个零,100000100末尾有2个零。

Input

  输入文件matrix.in的第一行包含两个正整数N,M表示矩阵大小。
  接下来N行每行M个正整数给出整个矩阵。

Output

  输出文件名为matrix.out。包含一个整数表示所求最小值。

Sample Input

3 3

1 2 3

10 5 100

10 8 9

Sample Output

1

Data Constraint

Hint

【数据规模和约定】
30%的数据满足 N , M ≤5;
100%的数据满足 1 < N, M ≤ 1000,所有输入数据不超过32位整范围。




这道题是要求从左上角走到右下角所经过的数字的积末尾的0的数量最小


一开始想到搜索,但是我DFS爆搜只能过到N=16&M=16,之后就机智滴想到了DP,


先把每个数的因数2和因数5的个数统计出来,


设状态F[i,j,1]表示从1,1到i,j尾数最少0的数的因数2的个数,F[i,j,2]表示从1,1到i,j尾数最少0的数的因数5的个数,那么得出状态:

tx1:=f[i-1,j,1]+b[i,j]; tx2:=f[i-1,j,2]+c[i,j];

ty1:=f[i,j-1,1]+b[i,j];ty2:=f[i,j-1,2]+c[i,j];

f[i,j,1]:=min(min(f[i,j,1],tx1),ty1);

f[i,j,2]:=min(min(f[i,j,2],tx2),ty2);

其中,b[i,j]表示输入矩阵[i,j]的因数2的个数;

c[i,j]表示输入矩阵[i,j]的因数5的个数;

tx1表示当前F数组[i,j]上面的数的因数2的个数;

tx2表示当前F数组[i,j]上面的数的因数5的个数;

ty1表示当前F数组[i,j]左边的数的因数2的个数;

ty2表示当前F数组[i,j]左边的数的因数5的个数;


之后就可以愉快滴AC了!


参考代码:

请勿抄袭!请勿抄袭!请勿抄袭!重要的事情说三遍!

var     n,m,t,tx1,tx2,ty1,ty2:int64;
        i,j:longint;
        a,b,c:array[0..1001,0..1001]of int64;
        f:array[0..1001,0..1001,1..2]of int64;
function min(x,y:int64):int64;
begin
        if x<y then exit(x) else exit(y);
end;
begin
        readln(n,m);
        for i:=1 to n do
                for j:=1 to m do
                begin
                        read(a[i,j]);
                        t:=a[i,j];
                        while (t mod 2=0) and (t>0) do
                        begin
                                t:=t div 2;
                                b[i,j]:=b[i,j]+1;
                        end;
                        t:=a[i,j];
                        while (t mod 5=0) and (t>0) do
                        begin
                                t:=t div 5;
                                c[i,j]:=c[i,j]+1;
                        end;
                end;
        fillchar(f,sizeof(f),$7f);
        f[1,1,1]:=b[1,1]; f[1,1,2]:=c[1,1];
        for i:=1 to n do
                for j:=1 to m do
                begin
                        if (i=1) and (j=1) then continue;
                        tx1:=f[i-1,j,1]+b[i,j]; tx2:=f[i-1,j,2]+c[i,j];
                        ty1:=f[i,j-1,1]+b[i,j]; ty2:=f[i,j-1,2]+c[i,j];
                        f[i,j,1]:=min(min(f[i,j,1],tx1),ty1);
                        f[i,j,2]:=min(min(f[i,j,2],tx2),ty2);
                end;
        writeln(min(f[n,m,1],f[n,m,2]));
end.





第三题:

https://61.142.113.109/senior/#main/show/2542

题目描述:

对于排列(P1,P2,...,PN),定义(i,j)为逆序对当且仅当i < j且Pi > Pj。统计{1,2,...,N}的所有排列中,逆序对数量为M的排列数量。

Input

  输入文件count.in第一行包含两个正整数N,M。

Output

  输出文件count.out应包含一个整数,表示满足条件的排列数除以124567的余数。

Sample Input

3 1

Sample Output

2

Data Constraint

Hint

【数据规模和约定】
30%的数据, N ≤ 10;
100%的数据,0 < N,M ≤ 1000。


非常简单的找规律,递推公式:

f[i,j]:=f[i,j-1]+f[i-1,j]-f[i-1,j-i]


参考代码:

请勿抄袭!请勿抄袭!请勿抄袭!重要的事情说三遍!

var    n,m,i,j:longint;

       f:array[0..1000,-1..1000]of longint;

begin

       readln(n,m);

       for i:=1 to n do f[i,0]:=1;

       for i:=2 to n do

                for j:=0 to 1000 do

                begin

                if j<i then f[i,j]:=(f[i-1,j]+f[i,j-1])mod124567

                else f[i,j]:=(f[i-1,j]+f[i,j-1]-f[i-1,j-i]+124567)mod124567;

                if f[i,j]<0 then f[i,j]:=0;

                end;

       writeln(f[n,m]);

end.




第四题:

https://61.142.113.109/senior/#main/show/2543

题目描述:

 平面上给定N个两两不同的整点,统计以给定的点为顶点,且直角边平行于坐标轴的直角三角形数。

Input

  输入文件right.in第一行为一个整数N。
  以下N行,每行给出一个点的坐标。

Output

  输出文件名为right.out。输出一个整数表示统计结果。

Sample Input

4

0 0

0 1

1 0

1 1

Sample Output

4

Data Constraint

Hint

【数据规模和约定】
30%的数据满足 N ≤ 100;
50%的数据满足 N ≤ 1000;
100%的数据满足0 < N≤ 100000,所有坐标不超过32位整数范围。


如果用普通的O(n³)的方法,只有30分,而O(n²)也只能50分,所以要AC要再进一步优化

 

我们可以直接枚举两个点,然后枚举两个点,所在的列上顶点的个数-2则为这两个点所能贡献的答案.

 

但是由于坐标很大~所以,我们需要把列坐标的个数压一下位,压成一堆类似叫做连成的数具体如何进行请自行思考或看参考代码

 

压了列坐标之后,则可把行坐标排个序,然后直接枚举两个行相等的点,如果行不相等则可直接break

 

参考代码:

请勿抄袭!请勿抄袭!请勿抄袭!重要的事情说三遍!

var    n,i,j,k:longint;

       s:int64;

       a:array[0..100000,1..2]of longint;

       b,c,w,num:array[0..100000]of longint;

procedure kuaipai(l,r:longint);

var    i,j,mid,t:longint;

begin

       i:=l;j:=r;

       mid:=a[(l+r)div 2,2];

       repeat

                while a[i,2]<mid do inc(i);

                while a[j,2]>mid do dec(j);

                if i<=j then

                begin

                        t:=a[i,2];

                        a[i,2]:=a[j,2];

                        a[j,2]:=t;

                        t:=w[i];

                        w[i]:=w[j];

                        w[j]:=t;

                        inc(i);

                       dec(j);

                end;

       until i>j;

       if l<j then kuaipai(l,j);

       if i<r then kuaipai(i,r);

end;

procedure kuaipai2(l,r:longint);

var    i,j,mid,t:longint;

begin

       i:=l;j:=r;

       mid:=a[(l+r)div 2,1];

        repeat

                while a[i,1]<mid do inc(i);

                while a[j,1]>mid do dec(j);

                if i<=j then

                begin

                        t:=a[i,1];

                        a[i,1]:=a[j,1];

                        a[j,1]:=t;

                        t:=num[i];

                        num[i]:=num[j];

                        num[j]:=t;

                        inc(i);

                        dec(j);

                end;

       until i>j;

       if l<j then kuaipai2(l,j);

        if i<r then kuaipai2(i,r);

end;

begin

       readln(n);

       for i:=1 to n do

       begin

                readln(a[i,1],a[i,2]);

                w[i]:=i;

       end;

       kuaipai(1,n);

       a[0,2]:=a[1,2];

       k:=1;

       for i:=1 to n do

       begin

                if a[i,2]<>a[i-1,2] theninc(k);

                b[i]:=k;

                inc(c[k]);

       end;

       for i:=1 to n do

                num[w[i]]:=b[i];

       kuaipai2(1,n);

       for i:=1 to n-1 do

               for j:=i+1 to n do

                        if a[i,1]=a[j,1] then

                               s:=s+(c[num[i]]-1)+(c[num[j]]-1) else break;

       writeln(s);

end.


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值