SSL 模拟赛 总结(2017.10.25)

T1:

压岁钱:

有N个人,给出他们的名字跟过年收到的每一笔压岁钱,求压岁钱总和最多的童鞋是哪个。

压岁钱为不超过 100000 的整数
每一笔压岁钱中间用至少一个空格隔开
一行s总长不超过 255 个。
数据保证答案唯一
对 30%的数据: n ≤ 3
对 60%的数据: n ≤ 10
对 100%的数据:n ≤ 50

题解:
暴力枚举求出每个童鞋的压岁钱总和然后找最大值,
只需要注意一下字符与数字之间转化的细节即可。

时间复杂度:O(∑length(s))

var
   max,i,j,k,l,n:longint;
   t,s,d,cmax:string;

begin
    readln(n);
    for l:=1 to n do
      begin
          readln(s);
          k:=pos(' ',s);
          t:=copy(s,1,k-1);
          delete(s,1,k);
          s:=s+' ';
          k:=pos(' ',s);
          i:=0;
          while k<>0 do
          begin
              d:=copy(s,1,k-1);
              delete(s,1,k);
              val(d,j);
              i:=i+j;
              k:=pos(' ',s);
          end;
          if i>max then
           begin
               max:=i;
               cmax:=t;
           end;
      end;
    writeln(cmax);
end.

T2:

砍伐树木:

有N棵树,mxy可以锯掉所有的树比H高的部分(当然,树木不高于 H 米的部分保持不变)。mxy就得到树木被锯下的部分。

例:
一行树的高度分别为 20,15,10 和 17 米
mH为15米,
切割后树木剩下的高度将是 15,15,10 和 15 米
而 mxy 将
从第1棵树得到 5 米,从第4棵树得到 2 米,
共得到 7 米木材。

找出最大的整数高度 H,使得 ta 能得到的木材至少为 M 米。

对于 30%的数据, 1 ≤ N ≤ 10,1 ≤ M ≤ 30;
对于 70%的数据, 1 ≤ N ≤ 1000,1 ≤ M ≤ 10000;
对于 100%的数据,1≤ N ≤ 1000000,1 ≤ M ≤ 2000000000。
每棵树的高度,值均不超过 1 000 000 000
保证所有木材长度之和大于 M,因此必然有解

题解:
二分答案:
直接枚举肯定会炸:
我们发现答案的范围在[0..max{ai}]
所以我们去二分这个范围:
过分满足即>m,我们就向后二分
刚好满足就输出
没满足即 < m,我们就向前二分
如果没有刚好满足的情况,
二分以后的r就是最优的
时间复杂度:O(N* log(max{ai}))

var
    a:array [0..1000001] of longint;
    l,r,mid,ans,i,h,n:longint;
    m,j:int64;
begin
    readln(n,m);
    for i:=1 to n do
      begin
          read(a[i]);
          if a[i]>r then r:=a[i];
      end;
    l:=1;
    while l<=r do
    begin
         mid:=(l+r) div 2;
         j:=0;
         for i:=1 to n do
           if a[i]>mid then j:=j+(a[i]-mid);
         if j>m then l:=mid+1
                else if jthen r:=mid-1
                else begin
                          writeln(mid);
                          halt;
                     end;
    end;
    writeln(r);
end.

T3:

恶作剧:

给定一个数字字符串S,用最少次数的加法让字符串等于一个给定的目标数字。每次加 法就是在字符串的某个位置插入一个加号。在需要的所有加号都插入后,就象做普通加法那样来求值。
例:
考虑字符串”12”,做 0 次加法,我们得到数字 12。插入1个加号,我们得到1+2=3。因此,这个例子中,最少用 1 次加法就得到3。
考虑字符串”303”和目标数字6,最佳方法不是”3+0+3”,而是”3+03”。能这样做是因为 1 个数的前导0不会改变它的大小。

对于 16%的数据: 1 ≤ S 的长度 ≤ 5;
对于 80%的数据: 1 ≤ S 的长度 ≤ 10;
对于 100%的数据:1 ≤ S 的长度 ≤ 40,0 ≤ N ≤ 1000。

题解:
这题很多人用了记忆化,不过我用的是DP:
我们设f[i,j]表示S前i位变成了数j最少用了多少次加法。
然后我们很容易得到转移:
f[i,j]=f[k,j-l]+1
很明显,l这个数就是s[k+1..i]这个子串的数字形式!
所以我们用一个数组去预处理这些子串的数
然后就可以推过去了,注意一下初值还有预处理的任意一个数都不能>N即可

时间复杂度:O(S的长度*N)

var
    p:array [0..1001,0..1001] of longint;
    f:array [0..41,0..1001] of longint;
    a:array [0..1001] of longint;
    i,j,k,n,m:longint;
    s,t:string;

function min(aa,bb:longint):longint;
begin
    if aa>bb then exit(bb);
    exit(aa);
end;

begin
    readln(s);
    n:=pos(' ',s);
    t:=copy(s,n+1,length(s)-n);
    delete(s,n,length(s)-n+1);
    n:=length(s);
    val(t,m);
    for i:=1 to n do
      a[i]:=ord(s[i])-48;
    for i:=1 to n do
      for j:=i to n do
        if (p[i,j-1]*10+a[j]<=m) and (p[i,j-1]<>-1)
                             then p[i,j]:=p[i,j-1]*10+a[j]
                             else p[i,j]:=-1;
    for i:=1 to n do
      for j:=0 to m do
        f[i,j]:=maxlongint div 5;
    if p[1,1]=-1 then
     begin
         writeln(-1);
         halt;
     end;
    f[1,a[1]]:=0;
    for i:=2 to n do
      begin
          for j:=1 to i-1 do
            for k:=0 to m do
              if f[j,k]<>maxlongint div 5 then
               if p[j+1,i]<>-1 then
                 if k+p[j+1,i]<=m then
                    f[i,k+p[j+1,i]]:=min(f[i,k+p[j+1,i]],f[j,k]+1);
          if p[1,i]<>-1 then
             f[i,p[1,i]]:=0;
      end;
    if f[n,m]<>maxlongint div 5
                 then writeln(f[n,m])
                 else writeln(-1);
end.

T4:

卡牌游戏:

题目大意:
N 个 mxy 正在玩游戏:桌子上有 M 张卡片,这 M 张卡片分别有一个唯一的 1~M 的编号。N 个 mxy 在桌子上抢牌。每个人最后的得分是所得的所有卡片编号的乘积(如果一张卡片都没取,得分为 1)。
然而本体 mxy 把把都输,为了验证自己是不是真的是幸运 E,她决定检验一下是否有人在说谎。
给出T组N,M以及对应的N个Ai表示每个人报出的得分,不可能没有人说谎就输出Yes
可能没有人说谎就输出No

对于 30%的数据: N ≤ 3, M ≤ 10, Ai ≤ 100;
对于 100%的数据: N ≤ 5, M ≤ 100, Ai ≤ 50000, T ≤ 10;

题解:
这题就是裸搜+剪枝:
注意一下对于一个ai,以及我们搜到现在ci的值,
我们可以让它要满足一些条件才能往下做,这就是剪枝:
①因为ai mod ci必定=0,所以我们判断ai div ci mod j=0因为ci*p=ai,那么p必须是j的倍数!
②ci*j≤ai
③每次向后搜,避免重复的枚举!
然后注意标记走过的点去回溯~

var
    d:array [0..1001] of boolean;
    a:array [0..6] of longint;
    c:array [0..6] of longint;
    k,i,j,n,m,t:longint;
    f:boolean;

procedure dfs(op,dep:longint);
var
    i:longint;
begin
    if dep>n then f:=true;
    if f then exit;
    for i:=op to m do
      if (a[dep] div c[dep] mod i=0) and (not(d[i])) and (c[dep]*i<=a[dep]) then
        begin
             c[dep]:=c[dep]*i;
             d[i]:=true;
             if c[dep]=a[dep]
                then dfs(1,dep+1)
                else dfs(i+1,dep);
             d[i]:=false;
             c[dep]:=c[dep] div i;
        end;
end;

begin
    readln(t);
    for k:=1 to t do
      begin
          readln(n,m);
          for i:=1 to m do d[i]:=false;
          for i:=1 to n do c[i]:=1;
          for i:=1 to n do read(a[i]);
          f:=false;
          dfs(1,1);
          if f then writeln('No')
               else writeln('Yes');
      end;
end.

你可能感兴趣的:(pascal,暴力/枚举/模拟,深搜dfs,二分&三分,动态规划)