[CF852H]Bob and stages

题意:给出平面上(n)个点,要求选出(k)个点,使得这些点形成一个凸包,且凸包内部没有点,求最大面积。无解输出(0)

题解:枚举凸包最左的点(p),删除所有在(p)左边的点,然后把(p)定为原点。将所有点按极角排序,相邻两个点之间连边,那么会形成一个星状多边形,合法的凸包一定在这个多边形内部。

先考虑求出这张图的visibility graph,显然合法的凸包所有边都是visibility graph上的边。求法大概是逆时针枚举所有点,对于每个点维护一个队列维护未来可能加入的边,实际上是对于每个点(i),维护所有满足(ij)在visibility graph上,并且当前还没找到(k(k>i))使得(jk)在visibility graph上的(j),详见代码。复杂度(O(E))

考虑在visibility graph上DP。顺时针枚举所有点,设(f_{i,j,k})表示最后一条选取的边为(i,j),选了(k)条边的最大面积。转移时可以枚举一个(l),如果(i,j)(l,i)这两条边可以同时存在(不会使得凸包不满足凸性)则可以转移到(f_{l,i,k+1})。朴素DP复杂度(O(n^3k)),可以对于每个点将转移出去和进来的边分别排序后(其实根据求visibility graph的过程,这些边是已经排好序的)双指针+前缀和优化,复杂度(O(n^2k))

总复杂度(O(n^3k))

#include<bits/stdc++.h>
using namespace std;
const int N = 210;
typedef long long ll;
typedef double db;
#define pb push_back

int gi() {
  int x = 0, o = 1;
  char ch = getchar();
  while((ch < '0' || ch > '9') && ch != '-') {
    ch = getchar();
  }
  if(ch == '-') {
    o = -1, ch = getchar();
  }
  while(ch >= '0' && ch <= '9') {
    x = x * 10 + ch - '0', ch = getchar();
  }
  return x * o;
}

struct point {
  int x, y;
  db k;
  point(int x = 0, int y = 0): x(x), y(y) {
    k = atan2(y, x);
  }
  point operator-(const point &A) const {
    return point(x - A.x, y - A.y);
  }
  ll operator%(const point &A) const {
    return 1ll * x * A.y - 1ll * y * A.x;
  }
  bool operator<(const point &A) const {
    return k < A.k;
  }
} a[N], p[N];

int n, m, tt;
ll f[N][N][55], mx[55], ans = 0;
queue<int> q[N];
vector<int> E[N], G[N];

void add(int x, int y) {
  while(!q[x].empty() && (p[q[x].front()] - p[x]) % (p[y] - p[x]) < 0) {
    add(q[x].front(), y), q[x].pop();
  }
  G[x].pb(y), E[y].pb(x), q[y].push(x);
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("a.in", "r", stdin);
  freopen("a.out", "w", stdout);
#endif
  cin >> n >> m;
  for(int i = 1; i <= n; i++) {
    a[i].x = gi(), a[i].y = gi();
  }
  for(int s = 1; s <= n; s++) {
    tt = 0;
    for(int i = 1; i <= n; i++) if(a[i].x > a[s].x || (a[i].x == a[s].x && a[i].y > a[s].y)) {
        p[++tt] = a[i] - a[s];
      }
    sort(p + 1, p + tt + 1);
    for(int i = 1; i <= tt; i++) {
      E[i].clear(), G[i].clear();
      while(!q[i].empty()) {
        q[i].pop();
      }
    }
    for(int i = 1; i < tt; i++) {
      add(i, i + 1);
    }
    memset(f, 0xc0, sizeof(f));
    for(int i = tt; i; i--) {
      memset(mx, 0xc0, sizeof(mx));
      reverse(E[i].begin(), E[i].end());
      int cur = G[i].size() - 1;
      for(auto j : E[i]) {
        f[i][j][1] = p[j] % p[i];
        while(~cur && (p[j] - p[i]) % (p[G[i][cur]] - p[i]) < 0) {
          for(int k = 1; k < m; k++) {
            mx[k] = max(mx[k], f[G[i][cur]][i][k]);
          }
          --cur;
        }
        for(int k = 1; k < m; k++) {
          f[i][j][k + 1] = mx[k] + p[j] % p[i];
        }
      }
    }
    for(int i = 1; i <= tt; i++)
      for(auto j : E[i]) {
        ans = max(ans, f[i][j][m - 2]);
      }
  }
  printf("%.2lf
", 1.0 * ans / 2);
  return 0;
}

原文地址:https://www.cnblogs.com/gczdajuruo/p/10922492.html