• [考试]20150821


    1、前言

      今天是APIO2010的题目,主要目的依旧是练习搜索(当然说是这么说)。感觉总体而言还是可做的,前两道似乎都比较适合骗分。

    2、Commando 特别行动队

    大概题意:给出一个序列,请划分成若干个子段,要求每一个子段和x变换为ax^2+bx+c后的总和最大。(a,b,c均已知,a<0)

    总结:50分的暴力DP还是很好写的,但是由于常数问题可能会被卡成40分,着主要集中在变换值的计算。考试的时候写了个贪心,即根据二次函数的性质,在x=-b/2a存在最大值,故在向前枚举的时候当越过最大值之后可以break掉,而不用枚举到最前面。但是这个正确性明显不足。交到CodeVS上之后我被吓坏了,竟然后6个点全部AC,然而前4个点却WA了。机智的我决定根据数据大小分类讨论,结果评测的时候只有70分,最后3个点全部TLE。后来自己手动测试的时候发现确实T成鬼了,实在膜拜CodeVS的评测速度啊!2.8s的数据跑出了0.4s。

    题解:当然这道题是并不存在70分算法的。正解是斜率优化动态规划。【此处待补充!】

    代码略。

    3、Patrol 巡逻

    大概题意:给出一棵树,连k条边(k∈[1,2]),使得从1号节点遍历全图的步数最少。

    总结:k=1时固然好写,可以证得求得树上最长链即可(即树的直径),可得30分。而后k=2的情况,考场上没写,但是看其他同学的搞法之后觉得很好,虽然同样是不能保证正确性的贪心,但是分数就好看多了,所以以后还是一样,多想想一些稀奇古怪的得分方式吧。

    题解:直接求树的直径(30分);求出树的直径之后将这条链删除,再次求直径(贪心算法,66分);树形DP/BFS(100分)。为什么前者只有66分呢?因为选取两个环是要分类讨论的,一种为两个环互不相交,没有重边,也就是贪心所能达到的;还有一种为两个环存在公共边,但是易得这样同样可以转换为一个环的情况,也没有看到什么证明方法,其实画一画就知道了。第二次必须用树形DP。

    代码(66分):

    -----------------------------------------------------------------------------------------------------

    #include<cstdio>
    #include<cstring>
    #define MAXN 100005

    int max(int a,int b) { return (a>b)?a:b; }

    struct Edge
    {
      int v,next,flag;
    };
    Edge edge[MAXN*2];

    int h[MAXN],now=1,n,k;

    void addEdge(int u,int v)
    {
      now++;
      edge[now].v=v;
      edge[now].next=h[u];
      h[u]=now;
    }

    int q[MAXN],vis[MAXN],dep[MAXN],flag[MAXN],nte[MAXN*2],fa[MAXN*2],bye;

    void BFS(int now,int c1,int c2)
    {
      int head=1,tail=2; q[head]=now,vis[now]=1,dep[now]=0;
      if (c1) flag[now]=1;
      while (head!=tail)
      {
        for (int x=h[q[head]];x;x=edge[x].next)
        {
          if (vis[edge[x].v] || edge[x].flag) continue;
          nte[edge[x].v]=x,q[tail++]=edge[x].v,vis[edge[x].v]=1;
          dep[edge[x].v]=dep[q[head]]+1;
          if (c2) fa[x]=nte[q[head]];
          if (c1) flag[edge[x].v]=1;
        }
        head++;
      }
    }

    int s,t,maxDep;

    void getDep(int &now)
    {
      for (int i=1;i<=n;i++) if (maxDep<dep[i]) maxDep=dep[i],now=i;
      memset(dep,0,sizeof(dep)),memset(vis,0,sizeof(vis));
    }

    int work1()
    {
      BFS(1,0,0),getDep(s),maxDep=0;
      BFS(s,0,1),getDep(t);
      return n*2-maxDep-1;
    }

    int work2()
    {
      int r1=0,r2=0;
      BFS(1,0,0),getDep(s),maxDep=0;
      BFS(s,0,1),getDep(t);
      for (int x=nte[t];x;x=fa[x]) edge[x].flag=edge[x^1].flag=1;
      r1=maxDep,maxDep=0;
      for (int i=1;i<=n;i++)
      if (!flag[i])
      {
        BFS(i,1,0),getDep(s);
        if (!maxDep) continue;
        maxDep=0,BFS(s,0,0),getDep(t),r2=max(r2,maxDep),maxDep=0;
        if (bye) break;
      }
      return 2*n-r1-r2;
    }

    int u,v;

    int main()
    {
      freopen("patrol.in","r",stdin);
      freopen("patrol.out","w",stdout);
      scanf("%d %d",&n,&k);
      for (int i=1;i<=n;i++)
      {
        scanf("%d %d",&u,&v);
        addEdge(u,v),addEdge(v,u);
      }
      printf("%d",k==1?work1():work2());
      return 0;
    }

    -----------------------------------------------------------------------------------------------------

    4、Signaling 信号覆盖

    大概题意:给出n个点,每次取其中3个点作外接圆,记录在圆内或圆上的点个数,求所有情况的平均个数。

    总结:计算几何,码农题吧。不多去考虑100分情况,直接暴力40分,其实也有差别很大的代码,值得分析一下。其实标准的计算几何代码是需要一大堆预处理的,需要用特殊的形式去表示向量等单位,以及各种操作。但是这道题的话用了可能有点小题大做吧,而我为了熟悉一下还是去用了。那么还是只给出一个比较简洁的代码吧。

    题解略。

    代码(40分,from z123z123d):

    -----------------------------------------------------------------------------------------------------

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>

    #define M 1510
    #define dbl double
    using namespace std;
    int n, x[M], y[M], s, ans;
    dbl ox, oy, r;

    void get (int x1, int y1, int x2, int y2, dbl &ux, dbl &uy, dbl &sx, dbl &sy) {
      sx = (x1 + x2 + 0.0) / 2, sy = (y1 + y2 + 0.0) / 2;
      dbl vx = x2 - x1, vy = y2 - y1;
      ux = -vy, uy = vx;
    }

    int cmp (dbl x, dbl t) {if (t < x + 1e-5 && t > x - 1e-5) return 0; return t - x > 0? -1 : 1;}

    dbl dis (dbl x1, dbl y1, dbl x2, dbl y2) {return sqrt ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));}

    void sol (dbl a1, dbl b1, dbl c1, dbl a2, dbl b2, dbl c2, dbl &t) {
      if (cmp (a1, 0) == 0)
      swap (a1, a2), swap (b1, b2), swap (c1, c2);
      dbl c = a2 / a1;
      b1 *= c, c1 *= c;
      t = (c1 - c2) / (b1 - b2);
    }

    void pre (int a, int b, int c) {
      dbl x1, y1, ux, uy, x2, y2, vx, vy, t;
      get (x[a], y[a], x[b], y[b], ux, uy, x1, y1);
      get (x[b], y[b], x[c], y[c], vx, vy, x2, y2);
      sol (ux, -vx, x2 - x1, uy, -vy, y2 - y1, t);
      ox = x2 + t * vx, oy = y2 + t * vy, r = dis (x[a], y[a], ox, oy);
    }

    void check (int k) {
      if (cmp (dis (x[k], y[k], ox, oy), r) <= 0) s ++;
    }

    int main()
    {
      freopen("signaling.in","r",stdin),freopen("signaling.out","w",stdout);
      scanf ("%d", &n);
      for (int i = 1; i <= n; i++)
        scanf ("%d %d", &x[i], &y[i]);
      for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++)
          for (int k = j + 1; k <= n; k++) {
            pre (i, j, k);
            s = 0;
            for (int t = 1; t <= n; t++)
              check (t);
            ans += s;
          }
      printf ("%.3lf", (ans + 0.0) / (n * (n - 1) * (n - 2) / 6));
      return 0;
    }

    -----------------------------------------------------------------------------------------------------

  • 相关阅读:
    数据库表的主外键
    数据库条件查询及关系搭建
    MySQL数据库的基本认识与操作
    MySQL5.7安装详解及常见安装问题解决
    数据库介绍
    Sensor图像调试
    1.线性表
    发展建议
    typedef 与 #define 的区别
    音频处理
  • 原文地址:https://www.cnblogs.com/jinkun113/p/4749199.html
Copyright © 2020-2023  润新知