这场Gym全名是这个:2013-2014 CT S01E01: Extended 2000 ACM-ICPC East Central North America Regional Contest (ECNA 2000)
……太长了……于是直接用网址里的编号了……
其实这题代码还没写完……但是思路应该是对的,于是来挖个坑
贴个链接:codeforces.com/gym/100227,戳进一道题之后可以下载所有题目的pdf……
题意:
平面上有n个红点和m个蓝点,要求选出一个定点全部是红点的凸多边形,使得内部和边上都没有蓝点。求这样的多边形的最大面积,输出方案。n,m<=100。
解:
首先枚举所有三角形,然后判断三角形内有没有蓝点。
接下来枚举凸多边形上最左侧的一个点C。假想得到了一个凸多边形,从C向其他所有点连边,把这个凸多边形划分成一堆三角形。
对于一个以C为顶点的合法三角形ABC,可以连边A->B:vector(CA)×vector(CB)以及其反边。那么一个合法多边形(不一定凸,甚至不一定是简单多边形……)的面积就是从C出发再回到C的一个环的权值和的绝对值(除以2,这里就不管这么多了)。
但是这样是不行的……一来不能保证凸性,二来有正环上的图上怎么求最大环……我反正是不会做。
既然这样,是不是可以把图弄成DAG?
把所有点分成在C上面和在C下面的!对于上方的点,只有当叉积为负时才连边,下方相反。这样得到的就是DAG了~只要分别求出到上下两部分每个点的最长路,然后各枚举一个点就得到了一个(不一定凸)的多边形。而且方案也很好记录。
接下来考虑凸性。在DAG上DP的时候多记录一位,d[i][j]表示到了点i且上一个点是点j时的最长路。转移的时候判断一下是否满足凸性就好了……麻烦的地方在于合并。
对于一个点i求出所有合法的d[i][j],将所有的vector(ji)按照极角排序,并求出前缀最大值以及最大值的位置。接下来枚举上下各一个点,计算出极角,再在上下二分求出最值……方案也可以类似求。
这样整个复杂度就是O(m*n^3+n^3logn)……思维难度还是不错的……实现起来也挺麻烦的……好题啊~
UPD:才发现……根本没有必要排序二分啥的……枚举完上下的两点之后再O(n)找前驱就好了……总复杂度还是O(n^4)的。
代码:
由于有一些特殊情况,而且要输出方案,所以写了将近6kb,丑死了……
//CF Gym 100227 I #include <cstdio> #include <cstring> #include <algorithm> #include <climits> using namespace std; #define pair(x, y) make_pair(x, y) #define N 100 struct point { int x, y; inline void read() { scanf("%d%d", &x, &y); } } p[N + 1], block[N + 1]; int n, m; inline int cross(const point &o, const point &a, const point &b) { return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x); } struct edge { int next, node, w; } e[N * N + 1]; int head[N + 1], tot; bool valid[N + 1][N + 1][N + 1]; int dist[N + 1][N + 1], d[2][N + 1][N + 1], deg[N + 1]; inline void addedge(int a, int b, int w) { e[++tot].next = head[a]; head[a] = tot, e[tot].node = b, e[tot].w = w, ++deg[b]; } inline void initGraph() { memset(head, 0, sizeof head), tot = 0; memset(deg, 0, sizeof deg); } void solve(int S, int d[][N + 1], bool up = false) { static int q[N + 1]; int h = 0, t = 0; q[t++] = S, d[S][S] = 0; while (h < t) { int cur = q[h++]; for (int i = head[cur]; i; i = e[i].next) { int node = e[i].node; if (!--deg[node]) q[t++] = node; for (int j = 1; j <= n; ++j) { if (d[cur][j] == INT_MIN) continue; if (cur != S && (up ^ (cross(p[j], p[cur], p[node]) < 0))) continue; if (d[node][cur] < d[cur][j] + e[i].w) d[node][cur] = d[cur][j] + e[i].w; } } } } inline bool on(const point &a, const point &b, const point &c) { if (c.x < min(a.x, b.x) || c.x > max(a.x, b.x)) return false; if (c.y < min(a.y, b.y) || c.y > max(a.y, b.y)) return false; return true; } int main(int argc, char* argv[]) { #ifdef KANARI freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif scanf("%d", &n); for (int i = 1; i <= n; ++i) p[i].read(); scanf("%d", &m); for (int i = 1; i <= m; ++i) block[i].read(); for (int x = 1; x <= n; ++x) for (int y = x + 1; y <= n; ++y) for (int z = y + 1; z <= n; ++z) { bool ok = true; for (int i = 1; ok && i <= m; ++i) { int a = cross(p[x], block[i], p[y]); int b = cross(p[y], block[i], p[z]); int c = cross(p[z], block[i], p[x]); if (!a && on(p[x], p[y], block[i])) ok = false; if (!b && on(p[y], p[z], block[i])) ok = false; if (!c && on(p[z], p[x], block[i])) ok = false; if (a > 0 && b > 0 && c > 0) ok = false; if (a < 0 && b < 0 && c < 0) ok = false; } if (ok == true) { valid[x][y][z] = valid[x][z][y] = true; valid[y][x][z] = valid[y][z][x] = true; valid[z][x][y] = valid[z][y][x] = true; } } int ans = 0, nodes = 0; static int convex[N + 1]; for (int c = 1; c <= n; ++c) { for (int a = 1; a <= n; ++a) for (int b = 1; b <= n; ++b) if (valid[c][a][b] || a == c || b == c) dist[a][b] = cross(p[c], p[a], p[b]); else dist[a][b] = INT_MAX; static int q[N + 1]; int cnt = 0; for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) d[0][i][j] = d[1][i][j] = INT_MIN; for (int i = 1; i <= n; ++i) if (p[i].x >= p[c].x && p[i].y >= p[c].y) q[++cnt] = i; initGraph(); for (int x = 1; x <= cnt; ++x) for (int y = 1; y <= cnt; ++y) if (dist[q[x]][q[y]] < 0) addedge(q[x], q[y], -dist[q[x]][q[y]]); for (int x = 1; x <= cnt; ++x) if (q[x] != c) addedge(c, q[x], 0); solve(c, d[0], true); cnt = 0; for (int i = 1; i <= n; ++i) if (p[i].x >= p[c].x && p[i].y <= p[c].y) q[++cnt] = i; initGraph(); for (int x = 1; x <= cnt; ++x) for (int y = 1; y <= cnt; ++y) if (dist[q[x]][q[y]] > 0 && dist[q[x]][q[y]] != INT_MAX) addedge(q[x], q[y], dist[q[x]][q[y]]); for (int x = 1; x <= cnt; ++x) if (q[x] != c) addedge(c, q[x], 0); solve(c, d[1]); bool updated = false; pair <int, int> up, down; for (int x = 1; x <= n; ++x) { if (!(p[x].x >= p[c].x && p[x].y >= p[c].y)) continue; for (int y = 1; y <= n; ++y) { if (!(p[y].x >= p[c].x && p[y].y <= p[c].y)) continue; if (dist[x][y] == INT_MAX) continue; int xl = -1, xcur = INT_MIN, yr = -1, ycur = INT_MIN; if (x != c) { for (int i = 1; i <= n; ++i) if (cross(p[i], p[x], p[y]) <= 0 && xcur < d[0][x][i]) xcur = d[0][x][i], xl = i; } else xl = c, xcur = 0; if (xl == -1) continue; if (y != c) { for (int i = 1; i <= n; ++i) if (cross(p[x], p[y], p[i]) <= 0 && ycur < d[1][y][i]) ycur = d[1][y][i], yr = i; } else yr = c, ycur = 0; if (yr == -1) continue; int cur = xcur + ycur + abs(dist[x][y]); if (cur > ans) { ans = cur, updated = true; up = pair(x, xl), down = pair(y, yr); } } } if (updated) { nodes = 0; convex[++nodes] = c; if (up.first != c) convex[++nodes] = up.first; for (int x = up.second, y = up.first; x != c; ) { int cur = d[0][y][x] - (-dist[x][y]); convex[++nodes] = x, y = x; for (int i = 1; i <= n; ++i) if (d[0][x][i] == cur && dist[i][x] <= 0) { x = i; break; } } reverse(convex + 2, convex + nodes + 1); if (up.first != down.first && down.first != c) convex[++nodes] = down.first; for (int x = down.second, y = down.first; x != c; ) { int cur = d[1][y][x] - dist[x][y]; convex[++nodes] = x, y = x; for (int i = 1; i <= n; ++i) if (d[1][x][i] == cur && dist[i][x] >= 0) { x = i; break; } } reverse(convex + 2, convex + nodes + 1); } } printf("%d\n", nodes); if (nodes > 0) { printf("%d", convex[1]); for (int i = 2; i <= nodes; ++i) printf(" %d", convex[i]); printf("\n"); } fclose(stdin); fclose(stdout); return 0; }