2017.7.11 C组总结

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(第一)
这次,考得还算可以,明天继续保持(∩_∩)

posted @ 2017-07-12 08:07  BEYang_Z  阅读(125)  评论(0编辑  收藏  举报