jzoj 2017.10.06 模拟赛

来源:互联网 发布:c语言 property get 编辑:程序博客网 时间:2024/06/03 12:26

T1:
【普及模拟】石子游戏:
你在桌子上一共放了N个石子,当你放第i个石子必须遵循以下规则:
1.当i是奇数时:直接放在左数第i个位置上;
2.当i是偶数时:如果第i个石子和第i-1个石子颜色相同,直接放在第i个位置上;
否则把当前最右边的连续的颜色相同的石子全部用相反颜色的石子取代,然后在第i个位置上放下石子i。

例如,
现:○○●●○○○(○代表白石子,●代表黑石子)

第8个石子是白色,因为它和第7个石子颜色相同,就直接放在最右边就可以了,桌面上变成:○○●●○○○○;

第8个石子是黑的,因为和第7个颜色不同,就先把右边连续的3个白石子用黑石子代替,然后再放下第8个石子,变成:○○●●●●●●。

给你每个石子的颜色,输出最终桌面上白石子的数量。

1<=N<=100,000

题解:
不难发现这题就是模拟,但是直接做每次都去枚举修改时间复杂度最坏会达到O(N^2),
所以我们在这个思路上优化:
把一段连续的石子序列给记录它的l,r以及颜色,然后修改的时候修改这个记录的区间而不用逐个点去改!

var     a:array [0..100001,1..3] of longint;     i,j,k,l,x,n,ans:longint;begin     assign(input,'stone.in'); reset(input);     assign(output,'stone.out');rewrite(output);     readln(n);     j:=0;     for i:=1 to n do       begin            readln(x);            if odd(i) then               begin                    inc(j);                    a[j,1]:=i;                    a[j,2]:=i;                    a[j,3]:=x;               end            else if a[j,3]=x                 then inc(a[j,2])                 else begin                           a[j,2]:=i;                           a[j,3]:=x;                      end;            l:=0;            for k:=j-1 downto 1 do              if a[k,3]=a[k+1,3] then              begin                   a[k,2]:=a[k+1,2];                   inc(l);              end;            j:=j-l;       end;     ans:=0;     for i:=1 to j do       if a[i,3]=0 then ans:=ans+(a[i,2]-a[i,1]+1);     writeln(ans);     close(input); close(output);end.

T2:
【普及模拟】公共子串:
题目大意:
给你两个字符串,计算最长的公共子串的长度。

字符串里的字符都是大写英文字母,长度不超过4000。

题解:
f[i,j]表示字符串s以s[i]结尾,t以t[j]结尾最长的公共子串的长度。
条件: s[i]=t[j]
转移: f[i,j]=f[i-1,j-1]+1

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

var    f:array [0..4001,0..4001] of longint;    s,t,tt:ansistring;    ans,i,j:longint;function max(aa,bb:longint):longint;begin    if aa>bb then exit(aa);    exit(bb);end;begin    assign(input,'common.in'); reset(input);    assign(output,'common.out');rewrite(output);     readln(s);     readln(t);     for i:=1 to length(s) do       for j:=1 to length(t) do       begin            if s[i]=t[j] then               f[i,j]:=f[i-1,j-1]+1;            ans:=max(f[i,j],ans);       end;     writeln(ans);    close(input);close(output)end.

T3:
【普及模拟】射击:
题目大意:
你可以射击4次,目标靶被分成N部分,每一部分的分值为P1,….,PN,你的总分为每次射击的分数之和,如果S不超过M,那你的分数就是S;否则如果S超过M,那么你的分数变为0。
给你每部分的分值以及M的值,计算你能得到的最大得分。

1<=N<=1000
1<=M<=200000000=2*10^8
1<=Pi<=100000000=10^8

题解:
排序+指针:
因为是射4次每次都可以在N个里面找一个,所以我们先将N个数任意2个相加的情况都求出来;
然后我们排序一下去用一个指针去做:
一个从小到大枚举一个从大到小枚举
找一个2个数+2个数的最大的不超过M的组合

时间复杂度:O(N^2+M)

var        a:array [0..1000001] of longint;        d:array [0..1001] of longint;        i,j,aa,cc,n,m,ans:longint;procedure qsort(l,r:longint);var        i,j,t,mid:longint;begin        if l>=r then exit;        i:=l;  j:=r;        mid:=a[(l+r) div 2];           repeat                while a[i]>mid 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;begin    assign(input,'dart.in');    assign(output,'dart.out');      reset(input); rewrite(output);      readln(n,m);       for i:=1 to n do readln(d[i]);        aa:=1; a[1]:=0;        for i:=1 to n do              begin                   inc(aa);                   a[aa]:=d[i];                   for j:=i+1 to n do                      begin                          inc(aa);                          a[aa]:=d[i]+d[j];                      end;              end;          qsort(1,aa);          i:=1; j:=aa; ans:=0;          while (i<=aa) and (j>=1) do          begin                if a[i]+a[j]>m                then inc(i)                else  begin                        if a[i]+a[j]>ans                            then ans:=a[i]+a[j];                        dec(j);                      end;          end;      writeln(ans);    close(input); close(output);end.

T4:
【普及模拟】过河:
题目大意:
一条河被人们看作N*N的矩阵,
人们有两种方法:一种是普通跳跃和快速跳跃,
快速跳跃的次数不得超过M。
普通跳跃中,他们能从当前石头跳到下一行的某个石头,
快速跳跃使得他们能从一个石头跳到下一行的下一行某个石头处。出发的一边的下一行是第一行,第N行的下一行是对岸。
每次跳跃的危险系数计算方法如下:
(当前石头的危险系数+下一个到达石头的危险系数)*(水平距离)
计算方法不考虑跳跃的种类,水平距离是两个石头所在列的差的绝对值,从岸边跳到某个石头或者从石头跳到岸边的危险系数为0。
给定N和M的值和每行石头的数量ki位置aij以及危险系数bij,计算从起始边到对岸最小的危险系数总和。输入保证一定可以到达对岸。

2<=N<=150,0<=M<=(n+1)/2
0<=K_i<=10
1<=列的值,危险系数<=1000

题解:
这题的DP很容易找:
我们由题意可以得出假设,
f[i,j,k]表示起始边到第i行第j个石头快速跳了k次的最小危险系数总和。
然后我们发现f[i,j,k]跟f[i-1,?,k],f[i-2,?,k-1]有关
我们就可以推出
f[i,j,k]=min
{min(f[i-1,l,k]+水平距离*危险系数和)
min(f[i-2,ll,k-1]+水平距离和*危险系数和)}
最后在f[n-1],f[n]中找一个最小值
一开始我只在f[n]里面找。。。
然后注意初值即可

时间复杂度:O(∑ki*M)

var    f:array [-10..151,0..11,-76..76] of longint;    a:array [0..151,0..11,1..2] of longint;    b:array [0..151] of longint;    ans,i,j,k,l,p,x,n,m:longint;function min(aa,bb:longint):longint;begin    if aa>bb then exit(bb);    exit(aa);end;begin    assign(input,'river.in'); reset(input);    assign(output,'river.out');rewrite(output);    readln(n,m);    for i:=1 to n do      begin            read(b[i]);            for j:=1 to b[i] do              read(a[i,j,1],a[i,j,2]);            readln;      end;    for i:=2 to n do      for j:=1 to 10 do        for k:=0 to m do f[i,j,k]:=maxlongint div 3;    for i:=1 to b[2] do    begin        for j:=1 to b[1] do           f[2,i,0]:=min(f[2,i,0],(a[1,j,2]+a[2,i,2])*abs(a[1,j,1]-a[2,i,1]));        f[2,i,1]:=0;    end;    for i:=3 to n do      for j:=1 to b[i] do        for k:=0 to m do          begin              for l:=1 to b[i-1] do                f[i,j,k]:=min(f[i,j,k],f[i-1,l,k]+(a[i,j,2]+a[i-1,l,2])*abs(a[i,j,1]-a[i-1,l,1]));              if k>0 then               for l:=1 to b[i-2] do                 f[i,j,k]:=min(f[i,j,k],f[i-2,l,k-1]+(a[i,j,2]+a[i-2,l,2])*abs(a[i,j,1]-a[i-2,l,1]));          end;    ans:=maxlongint;    for i:=1 to b[n] do      for j:=0 to m do        ans:=min(f[n,i,j],ans);    for i:=1 to b[n-1] do      for j:=0 to m-1 do        ans:=min(f[n-1,i,j],ans);    writeln(ans);    close(input); close(output);end.