zoukankan      html  css  js  c++  java
  • 精确狙击

    小葱同学今天学会了复制,于是我们现在有N个小葱同学。N个小葱同学每人有一把弹弓,并且第i个小葱同学站在((x_i, y_i))的位置上。现在所有的小葱同学希望把弹弓的射程调到某一个值,使得在这个射程下,存在M个小葱同学,这M个小葱同学之之间任意两位都可以互相射击到。问这个距离最小是多少。

    【输入格式】

    第一行两个数N,M

    接下来N行每行两个整数代表小葱同学的坐标

    【输出格式】

    输出一行一个六位小数代表答案

    【样例】

    4 3

    0 0

    0 1

    1 1

    1 0

    【输出】

    1.414212

    【数据规模与约定】

    对于30%的数据,(N leq 10)

    对于另外10%的数据,(M = 2)

    对于另外20%的数据,(M = 3)

    对于80%的数据,(M leq 50)

    对于100%的数据,(1 leq M leq N leq 200, |x|, |y| leq 10^4)

    Solution

    看题意似乎是让我们求一个最大团,但这在普通图里是个NPC问题,那有什么特殊图能做吗?

    二分图可以。因为二分图相当于两个点集,每个点集之内的点相互之间都有边,两个点集之间又有一些边。所有就相当于要找一个最大匹配。这个显然可以求。

    但是,哪里来的二分图呢?

    考虑这是一个坐标图,任意两点的连线会有上面的一部分和下面的一部分。当我们选中两个点,并以两个点的距离为半径画圆的时候,这两个圆会有一个相交的集合。将这个集合分成两半,一半在两点连线的上面,一半在下面。这两个点集的内部的点的距离一定小于等于选中两点之间的距离,而两个点集之间的点不一定。所以这就形成了一个二分图。

    至于怎么判断某个点是属于上面一个集合还是下面的,用向量的叉乘就好了。

    所有,我们首先二分一个距离,然后找到所有满足距离小于等于这个距离的点对,再以每个点对画圆,构建二分图,统计每个二分图的最大匹配。如果这个匹配大于等于M,就说明OK,反之不行。

    这里要注意一点,二分图不能直接求最大匹配,因为我们实际上要求的是一个最大团,所以要求一个反图的最大匹配。最大团 = 反图最大独立集 = 总点数 - 反图最大匹配

    这题就这样做完了qwq

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    int n, m, l, r, p[2][210], re[210], x[210], y[210];
    bool use[210];
    int c, dis[210][210], d[210 * 210];
    inline int cross(int x1, int y1, int x2, int y2) {//叉乘
      return x1 * y2 - x2 * y1;
    }
    inline bool dfs(int now) {//求最大匹配
      for (int i = 1; i <= r; ++i)
        if (!use[i] && dis[p[0][now]][p[1][i]] > c) {//因为是反图,所以找距离大于c的
          use[i] = 1;
          if (!re[i] || dfs(re[i])) {
            re[i] = now; return 1;
          }
        }
      return 0;
    }
    inline bool check(int d) {
      if (m == 1) return 1;
      for (int i = 1; i <= n; ++i)
        for (int j = i + 1; j <= n; ++j)
          if (dis[i][j] <= d) {
            if (m == 2) return 1;
            l = 0, r = 0;
            for (int k = 1; k <= n; ++k)
              if (k != i && k != j && dis[i][k] <= dis[i][j] && dis[j][k] <= dis[i][j]) {
                  //判断这个节点是否在两个圆的交集内,并划分集合
                if (cross(x[k] - x[i], y[k] - y[i], x[j] - x[i], y[j] - y[i]) <= 0) p[0][++l] = k;
                else p[1][++r] = k;
              }
            c = d;
            memset(re, 0, sizeof re);
            int ans = 0;
            for (int k = 1; k <= l; ++k) {//求匹配
              memset(use, 0, sizeof use);
              if (dfs(k)) ans++;
            }
            ans = l + r - ans + 2;//统计答案
            if (ans >= m) return 1;
          }
      return 0;
    }
    int main() {
      freopen("die.in", "r", stdin);
      freopen("die.out", "w", stdout);
      scanf("%d%d", &n, &m);
      if (m == 1) {
        printf("0.000000
    "); return 0;
      }
      for (int i = 1; i <= n; ++i)
        scanf("%d%d", &x[i], &y[i]);
      int cnt = 0;
      for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j) {//计算出两点之间的距离
          dis[i][j] = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
            //直接存储平方
          d[++cnt] = dis[i][j];//二分的数组
        }
      sort(d + 1, d + 1 + cnt);
      int l = 0, r = cnt, u = cnt;
      while (l < r) {
        int mid = (l + r) >> 1;
        if (check(d[mid])) u = mid, r = mid - 1;
        else l = mid + 1;
      }
      printf("%.6lf
    ", sqrt(d[u]));
      return 0;
    }
    
  • 相关阅读:
    Educational Codeforces Round 13 E. Another Sith Tournament 概率dp+状压
    Codeforces Round #358 (Div. 2) D. Alyona and Strings 字符串dp
    Codeforces Round #359 (Div. 2) D. Kay and Snowflake 树的重心
    Codeforces Round #311 (Div. 2) D. Vitaly and Cycle 奇环
    Codeforces Round #Pi (Div. 2) E. President and Roads 最短路+桥
    Codeforces Gym 100342J Problem J. Triatrip 三元环
    HDU 4587 TWO NODES 割点
    hdu 5615 Jam's math problem(十字相乘判定)
    C++数组作为函数参数的几个问题(转)
    UVA
  • 原文地址:https://www.cnblogs.com/kylinbalck/p/11808798.html
Copyright © 2011-2022 走看看