CF Gym 100227 I题 题解

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



你可能感兴趣的:(图论,codeforces,Gym)