2017.7.11 C组总结

来源:互联网 发布:电脑gif制作软件 编辑:程序博客网 时间:2024/06/08 12:23

NO.1

题目描述:知道n+1(包括Oliver)个人的语文、数学、英语成绩,求出Oliver的数学、语文、英语分别和最高的分数差多少?

思路:高精+排序
考试一瞟数据最长的成绩<30位,便无奈至极,于是手动码起了高精
因为他只用求出与最大的分差,所以可以用排序,将三科的最大成绩求出来
再做三次高精减就可以A
时间复杂度O(3*30+3nlogn)

代码:

uses math;const maxn=30;type arr=array[0..10001]of string;var x1,y1,z1:string;    x,y,z:array[0..10001]of string;    n,i:longint;procedure qsort(var x:arr;l,r:longint);var i,j:longint;    mid:string;begin  if l>=r then exit;  i:=l; j:=r; mid:=x[(l+r) div 2];  repeat    while (length(x[i])=length(mid))and(x[i]>mid)or(length(x[i])>length(mid)) do inc(i);    while (length(x[j])=length(mid))and(x[j]<mid)or(length(x[j])<length(mid)) do dec(j);    if i<=j then      begin        x[0]:=x[i];        x[i]:=x[j];        x[j]:=x[0];        inc(i);        dec(j);      end;  until i>j;  qsort(x,l,j);  qsort(x,i,r);end;procedure jian(x,y:string);var i,g,l:longint;    l1,l2,l3:array[0..30]of longint;begin  fillchar(l1,sizeof(l1),#0);  fillchar(l2,sizeof(l2),#0);  fillchar(l3,sizeof(l3),#0);  for i:=1 to length(x) do l1[maxn-length(x)+i]:=ord(x[i])-48;  for i:=1 to length(y) do l2[maxn-length(y)+i]:=ord(y[i])-48;  for i:=maxn downto 1 do    begin      g:=l1[i];      if g<l2[i] then        begin          l1[i-1]:=l1[i-1]-1;          g:=g+10;        end;      g:=g-l2[i];      l3[i]:=g mod 10;    end;  l:=1;  while (l3[l]=0)and(l<=maxn) do inc(l);  for i:=l to maxn do write(l3[i]);  write(' ');end;begin  assign(input,'score.in');  assign(output,'score.out');  reset(input);  rewrite(output);  readln(x1);  readln(y1);  readln(z1);  readln(n);  for i:=1 to n do    begin      readln(x[i]);      readln(y[i]);      readln(z[i]);    end;  qsort(x,1,n);if (length(x1)=length(x[1]))and(x1>=x[1])or(length(x1)>length(x[1])) then write('0 ')  else jian(x[1],x1);  qsort(y,1,n);if (length(y1)=length(y[1]))and(y1>=y[1])or(length(y1)>length(y[1])) then write('0 ')  else jian(y[1],y1);  qsort(z,1,n);if (length(z1)=length(z[1]))and(z1>=z[1])or(length(z1)>length(z[1])) then write('0 ')  else jian(z[1],z1);  close(input);  close(output);end.

NO.2

题目描述:有一棵技能树,每个节点都是一个技能,学习一个技能,才能学会它的后继技能,每项技能都有不同的等级,不同的等级耗不同的技能点,和拥有不同的分数。要求出最大的分数

思路:树形DP+模拟
乍一看,1<=n<=20,便想到搜索,旁边的黄大佬便码起了搜索,结果只有20!!!
听了dalao的讲解,
首先,设l1[i][j]表示为第i个技能j级的位置(即输入顺序)
设a[i][0]表示技能i的前继技能和上一级的个数
a[i][j]表示技能i的第j级的前继技能和上一级的位置
要求出对于任何一个结点的后继结点和前继结点、相应的技能点(f[i])和分数(w[i])
然后,对于i的父亲名字==j的名字(就是j是i的前继技能),那么a[l1[j][1]][0]++,a[l1[++y,1],a[l1[y,1],0]]=l1[i,1](就是求出对于i结点的前继技能)
于是要造一棵树,表示对于每个结点的要学的两个端点(也就是说对于i结点要学的技能就在[l..r]这个区间内)
设dp[i][j]表示为从i出发,用了j技能点的最大分数值,那么状态转移方程就是dp[x][i]=max(dp[r[x]][i],dp[l[x]][j]+dp[r[x]][i-j-f[x]]+w[x])
0<=i<=p,1<=j<=i-f[x]

代码:

uses math;var n,m,x,y,s,root,p,i,j:longint;    l1:array[0..20,0..20]of longint;    l,r,f,w:array[0..400]of longint;    a:array[0..400,0..400]of longint;    dp:array[-1..400,0..100]of longint;    name,fname:array[0..20]of string;procedure build(x:longint);var i:longint;begin  for i:=1 to a[x,0]-1 do r[a[x,i]]:=a[x,i+1];  if (a[x,0]>0) then l[x]:=a[x,1];  for i:=1 to a[x,0] do build(a[x,i]);end;procedure treedp(x:longint);var i:longint;begin  if x=-1 then exit;  treedp(l[x]);  treedp(r[x]);  for i:=0 to p do    begin      dp[x,i]:=dp[r[x],i];      for j:=0 to i-f[x] do        dp[x,i]:=max(dp[x,i],dp[l[x],j]+dp[r[x],i-j-f[x]]+w[x]);    end;end;begin  assign(input,'skill.in');  assign(output,'skill.out');  reset(input);  rewrite(output);  readln(n);  for i:=1 to n do    begin      readln(name[i]);      readln(fname[i]);      readln(s);      for j:=1 to s do        begin          inc(x);          l1[i,j]:=x;          read(y);          f[x]:=y;          if j>1 then            begin              inc(a[l1[i,j-1],0]);              a[l1[i,j-1],a[l1[i,j-1],0]]:=l1[i,j];            end;        end;      readln;      for j:=1 to s do        begin          read(y);          w[l1[i,j]]:=y;        end;      readln;    end;  for i:=1 to n do    begin      y:=root;      for j:=1 to n do        if (fname[i]=name[j]) then          begin            y:=j;            break;          end;      inc(a[l1[y,1],0]);      a[l1[++y,1],a[l1[y,1],0]]:=l1[i,1];    end;  readln(p);  for i:=1 to n do    begin      read(y);      for j:=1 to y do        begin          w[l1[i][j]]:=0;          f[l1[i][j]]:=0;        end;    end;  for i:=0 to x do    begin      l[i]:=-1;      r[i]:=-1;    end;  build(root);  treedp(root);  writeln(dp[root,p]);  close(input);  close(output);end.

NO.3

题目描述:用m个人n个物品,每个人的背包是无限大的,但是每件物品在每个背包只能装一个,而且每个背包都不完全相同,问最大的物品价值和是多少

思路:贪心+递推
因为每件物品在每个背包只能装一个,那么设f[i]来表示分数值为i的情况有多少种
递推式就是f[i]=f[i-w[i]]+f[i]
然后downto,用贪心得出最大可能性ans,这里的贪心有两种操作:
①如果f[i]<=m
ans=ans+i*f[i];
m=m-f[i]
②如果f[i]>m
ans=ans+i*m
break

代码:

#include<cstdio>#include<iostream>using namespace std;unsigned long long ans,sum=0,f[25100];long n,m,w[510];int main(){    freopen("team.in","r",stdin);    freopen("team.out","w",stdout);    scanf("%d%d",&m,&n);    for (int i=1;i<=n;i++)    {        scanf("%d",&w[i]);        sum=sum+w[i];    }    f[0]=1;    for (int i=1;i<=n;i++)    {        for (int j=sum;j>=0;j--) if (j>=w[i]) f[j]+=f[j-w[i]];    }    ans=0;    for (int i=sum;i>=0;i--)        if (f[i]<=m)        {            m-=f[i];            ans=ans+f[i]*i;        }        else        {            ans=ans+i*m;            break;        }    printf("%lld",ans);    fclose(stdin);    fclose(stdout);    return 0;}

NO.4

题目描述:有一串珠子,每个珠子有一个能量值E[i],除了前后两个珠子,其他的都符合Ei=(Ei-1+Ei+1)/2+Di,现在知道全部Di、E1和En,让你求出E2~E(n-1)

思路:数学
知道:2E2-E1-E3=2D2 ①
2E3-E2-E4=2D3 ②
2E4-E3-E5=2D4 ③
……
然后,②*2-① 3E3-2E4=E1+2D2+4D3
③*3-② 4E4-3E5=E1+2D2+4D3+6D4
……
可以得到规律k*Ek-(k-1)*Ek+1=E1+2D2+4D3+…+(k-1)*2*Dk
把n-1当k代入得(N-1)*EN-1-(N-2)*EN=E1+2D2+4D3+…+(N-2)*2*DN-1
然后就可以发现这个式子里只有一个未知数,直接求就好了

代码:

#include<cstdio>#include<iostream>using namespace std;long long n,e[500000],a[500000],d[500000];int main(){    freopen("fett.in","r",stdin);    freopen("fett.out","w",stdout);    scanf("%lld%lld",&n,&e[1]);    scanf("%lld",&e[n]);    for (int i=2;i<=n-1;i++) scanf("%lld",&d[i]);    a[2]=0;    a[3]=(a[2]-d[2])*2-e[1];    for (int i=4;i<=n;i++) a[i]=(a[i-1]-d[i-1])*2-a[i-2];    e[2]=(e[n]-a[n])/(n-1);    for (int i=3;i<=n;i++) e[i]=(e[i-1]-d[i-1])*2-e[i-2];    for (int i=1;i<=n;i++) printf("%lld ",e[i]);    fclose(stdin);    fclose(stdout);    return 0;}

这次比赛,100(高精)+0+100(递推+贪心)+100(手动推公式)=300(第一)
这次,考得还算可以,明天继续保持(∩_∩)