2017.8.08 SSL 模拟赛

T1:
分数拆分split:
题目大意:
给出一个正整数k,求所有的正整数对(x,y),使得:1/k = 1/x + 1/y,且 x>=y。求有多少组这样的解。

数据限制:
50%的数据满足:2<=k<=1000;
100%的数据满足:2<=k<=3000
题解:
这题我们分析一下:
对于1/k=1/x+1/y,我们可以分析得出,只需要枚举出其中x或者y的可能,就可以找到全部的解。
对于x>=y,我们再分析,可以得出
y必定<=1/k的一半
则 y<=2*k //因为是分数
然后因为 1/y只是1/k的一个加数,所以y必定>k
则 y>=k+1
因此我们可以在[k+1..2*k]枚举y的可能
然后我们如何去判断是否成立呢?
对于每个枚举到的i:
我们要用到1/i,1/k,这时候会有精度问题,
我们就可以转化成 k/k*i i/k*i,没毛病
然后因为1/x=1/k-1/y
所以 1/x=i/k*i-k/k*i
=i-k/i*k
这时候,因为i-k/i*k要满足条件
i*k mod (i-k)=0这时候才能化简成1/x的形式
所以我们判断一下,符合条件的则是可以作为解的,累加答案。

时间复杂度:O(K)

var
    k,ans,i,j:longint;

begin
    assign(input,'split.in'); reset(input);
    assign(output,'split.out');rewrite(output);
    readln(k);
    for i:=k+1 to 2*k do
      if (k*i) mod (i-k)=0
         then inc(ans);
    writeln(ans);
    close(input); close(output);
end.

T2:
2、字符串展开expand:
题目大意:
我们需要对字符串进行展开:
(1)对于参数p1,为展开方式:
①p1=1时,对于字母子串,填充小写字母;
②p1=2时,对于字母子串,填充大写字母;
这两种情况下数字子串的填充方式相同。
③p1=3时,子串都用要填充的字母个数相同的星号“*”来填充。
(2)对于参数p2:
①填充字符的重复个数。
p2=k 表示同一个字符要连续填充 k 个。
②“-”号两侧不变。
(3)对于参数p3,为是否改为逆序:
①p3=1 表示维持原有顺序;
②p3=2 表示采用逆序输出;
注意这时仍然不包括减号两端的字符。
(4)
①若减号右边的字符恰好是左边字符的后继,只删除中间的减号,且输出它们在ASCII码表中的它们之间包含的字符;
②若减号右边的字符按照 ASCII 码的顺序小于或等于左边字符,输出时,要保留中间的减号。

数据限制:
40%的数据满足:字符串长度不超过5;
100%的数据满足:
1<=p1<=3, 1<=p2<=8, 1<=p3<=2。
输入的字符串长度不超过 10
题解:
模拟:
这题没什么好讲的,除了判断条件多一点,就没什么难的。注意一下细节,照着题意要求去做,就可以了。

时间复杂度:O(输出字串长度)

var
     i,j,k,l,n,p1,p2,p3:longint;
     f,g,s:string;

function check(o:char):boolean;
begin
    if o in ['0'..'9'] then exit(true);
    if o in ['a'..'z'] then exit(true);
    if o='-' then exit(false);
end;

function flag(x,y:char):boolean;
begin
    if x>=y then exit(false);
    if (x in ['0'..'9']) and (y in ['0'..'9']) then exit(true);
    if (x in ['a'..'z']) and (y in ['a'..'z']) then exit(true);
    exit(false);
end;
begin
    assign(input,'expand.in');
    assign(output,'expand.out');
    reset(input); rewrite(output);
     readln(p1,p2,p3);
     readln(s);
     for i:=1 to length(s) do
         if check(s[i]) then write(s[i])
            else begin
                    if (flag(s[i-1],s[i+1])=false) or ((i=1) and (s[i]='-')) then write('-')
                    else begin
                           if ord(s[i-1])+1<=ord(s[i+1])-1 then
                              begin
                                 g:='';
                                 if p1=1 then
                                    begin
                                       for j:=ord(s[i-1])+1 to ord(s[i+1])-1 do
                                           for k:=1 to p2 do g:=g+lowercase(chr(j));
                                    end;
                                 if p1=2 then
                                    begin
                                       for j:=ord(s[i-1])+1 to ord(s[i+1])-1 do
                                           for k:=1 to p2 do g:=g+upcase(chr(j));
                                    end;
                                 if p1=3 then
                                    begin
                                       for j:=ord(s[i-1])+1 to ord(s[i+1])-1 do
                                           for k:=1 to p2 do g:=g+'*';
                                    end;
                                 if p3=2 then for j:=length(g) downto 1 do write(g[j])
                                         else write(g);
                              end;
                        end;
              end;
     close(input); close(output);
end.

T3:
两人过桥bridge:
题目大意:
有n个人在晚上要通过一座桥。任何时刻最多只能有两个人在桥上,并且必须要带着手电筒才能通过,只有一个手电筒,每个人都有不同的过桥时间,两个人一起过桥所用的时间等于其中较慢的一个。求最短时间内使所有人都过桥的方案所需时间。

数据限制:
每个人的过桥时间不超过100秒。
手电筒必须由人带回,不可以从对岸扔过去
40%的数据满足:n<=100;
100%的数据满足:n<=1000;

题解:
DP:
设f[i]表示过去了i个人最少要花费的时间。
然后我们先对过桥时间进行个排序,这样才能保证最后求出来的是最优解。
然后就推:
f[i]=min(f[i-1]+a[i]+a[1],f[i-2]+a[i]+a[2]*2+a[1])
因为这里有2种策略可以去采用:
1.最快的来回送,则
①1过来
②1,a[i]过去
2.最快的送过来,最慢的2个一起过去,次快的过来,然后最快次快一起过去,则
①1过来
②a[i],a[i-1]过去
③2过来
④1,2一起过去

时间复杂度:O(N)

var
    a,f:array [0..1001] of longint;
    i,j,n:longint;

procedure qsort(l,r:longint);
var
    i,j,mid:longint;
begin
    if l>=r then exit;
    mid:=a[(l+r) div 2];
    i:=l; j:=r;
    repeat
         while a[i]do inc(i);
         while a[j]>mid do dec(j);
         if i<=j then
         begin
              a[0]:=a[i];a[i]:=a[j];a[j]:=a[0];
              inc(i); dec(j);
         end;
    until i>j;
    qsort(i,r);
    qsort(l,j);
end;

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

begin
    assign(input,'bridge.in'); reset(input);
    assign(output,'bridge.out');rewrite(output);
    readln(n);
    for i:=1 to n do readln(a[i]);
    qsort(1,n);
    f[1]:=a[1];
    f[2]:=a[2];
    for i:=3 to n do
      f[i]:=min(f[i-1]+a[i]+a[1],f[i-2]+a[i]+a[1]+a[2]*2);
    writeln(f[n]);
    close(input);close(output);
end.

T4:
4、迷之阶梯ladder:
题目大意:
科学家要进入遗迹,但需要通过一段迷之阶梯。
梯必须要按照它要求的方法,否则就无法登上阶梯:
它要求的方法有以下三个限制:
①如果下一步阶梯的高度只比当前阶梯高1,则可以直接登上。
②除了第一步阶梯外,都可以从当前阶梯退到前一步阶梯。
③当你连续退下k后,你可以一次跳上不超过当前阶梯高度 2^k的阶梯。例位于第j步阶梯,并且是从第j+k步阶梯退下来的。那么你可以跳到高度不超过梯高度+ 2^k的任何一步阶梯。

跳跃一次算一次移动。开始时你在第1步阶梯,如果能登上阶梯,请求出最小步数,否则输出-1。

对于 50%的数据: 1<=N<=20;
对于 100%的数据:1<=N<=200;
每步阶梯高度不超过 2^31-1

题解:
DP:
我们设f[i]表示到第i个阶梯需要的最少步数。
因为有2^K,且阶梯高度不超过2^31-1,所以我们可以直接预处理出2的次方。
然后我们分类讨论一下:
初值:
f[1]=0
其他取一个超大值表示未到达。
①当a[i]-a[i-1]=1即2个阶梯高度差1,可以直接走,则f[i]=min(f[i],f[i-1]+1)
②因为第i个阶梯,可能是由他前面的任意一个阶梯j
跳过来的,所以我们可以去枚举1~i-1,不过从第j个跳过来,不一定就后退了i-j步,这个要注意!
设后退了k步跳过来的,则可以推出
f[i]=min(f[i],f[j+k]+k+1),
因为是从第j+k个阶梯开始后退k步,最后起跳,所以方程这么得来
最后输出的时候,要判断一下,f[n]是否小于原来的最大值,我打等于就一直错,为什么呢?
因为在在方程中的某个阶梯i,它可以到达阶梯n,阶梯1,却并不能到达阶梯i,这时候,f[n]却加上了f[i],然后答案就会大于你原来赋值的最大值,就要判断一下。当然,你在方程中去特判,也可以。

时间复杂度:O(N^2*31)

var
    a,f:array [0..201] of longint;
    num:array [0..31] of int64;
    i,j,k,n:longint;

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

begin
    readln(n);
    for i:=1 to n do
    begin
        read(a[i]);
        f[i]:=maxlongint div 5;
    end;
      num[0]:=1;
      for i:=1 to 31 do
        num[i]:=num[i-1]*2;
      f[0]:=0;
      f[1]:=0;
      for k:=2 to n do
      begin
          if a[k]-a[k-1]=1
             then f[k]:=f[k-1]+1;
          for i:=k-1 downto 1 do
          begin
              for j:=1 to 31 do
                if (num[j]>=a[k]-a[i]) then
                   if i+j<=k-1 then
                      f[k]:=min(f[i+j]+j+1,f[k]);
          end;
      end;
   if f[n]div 5
      then writeln(f[n])
      else writeln(-1);
end.

你可能感兴趣的:(暴力/枚举/模拟,排序&拓扑,贪心,规律与思维,动态规划)