这场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;
}