您的位置:首页 > Web前端

BZOJ1591 USACO 2008 Dec Gold 4.Largest Fence Solution

2014-09-17 14:54 393 查看
题目大意:

求出平面上n个点组成的一个包含顶点数最多的凸多边形。n<=250.

Sol:

首先考虑O(n^4)算法。

将所有点按照水平序排序。

首先枚举最左侧的点,考虑在这种情况下down[i][j](表示最右侧的两个点为i,j)下凸壳上的最大点数。

与之同理,枚举最右侧的点,考虑此时up[i][j](表示最左侧的点i,j)上凸壳上的最大点数。

此时状态O(n^2),转移O(n),n次dp,总复杂度为O(n^4).

对于答案,我们只需枚举左右两个端点,O(n)合并dp最优值即可。总复杂度为O(n^3).

因此,总的时间复杂度为O(n^4).

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <algorithm>
using namespace std;

#define N 251
struct Point {
int x, y;
Point(int _x = 0, int _y = 0):x(_x),y(_y){}
bool operator < (const Point &B) const {
return x < B.x || (x == B.x && y < B.y);
}
Point operator - (const Point &B) const {
return Point(B.x - x, B.y - y);
}
void read() {
scanf("%d%d", &x, &y);
}
}S
;

int Cross(const Point &A, const Point &B) {
return A.x * B.y - A.y * B.x;
}
bool judge(const int &i, const int &j, const int &k) {
return Cross(S[i] - S[j], S[j] - S[k]) > 0;
}

int down

, up

, s_down

, s_up

;

int main() {
int n;
scanf("%d", &n);
int i, j, k;
for(i = 1; i <= n; ++i)
S[i].read();

sort(S + 1, S + n + 1);

memset(s_down, 0xef, sizeof(s_down));
memset(s_up, 0xef, sizeof(s_up));

for(int begin = 1; begin < n; ++begin) {
memset(down, 0xef, sizeof(down));
for(j = begin + 1; j <= n; ++j)
down[begin][j] = 2;
for(j = begin + 1; j <= n; ++j) {
for(i = begin + 1; i < j; ++i) {
for(k = begin; k < i; ++k) {
if (judge(k, i, j))
down[i][j] = max(down[i][j], down[k][i] + 1);
}
}
}
for(j = begin + 1; j <= n; ++j)
for(i = begin; i < j; ++i)
s_down[begin][j] = max(s_down[begin][j], down[i][j]);
}

for(int end = n; end > 1; --end) {
memset(up, 0xef, sizeof(up));
for(j = 1; j < end; ++j)
up[end][j] = 2;
for(j = end - 1; j >= 1; --j) {
for(i = j + 1; i < end; ++i) {
for(k = i + 1; k <= end; ++k) {
if (judge(k, i, j))
up[i][j] = max(up[i][j], up[k][i] + 1);
}
}
}
for(j = end - 1; j >= 1; --j)
for(i = j + 1; i <= end; ++i)
s_up[end][j] = max(s_up[end][j], up[i][j]);
}

int res = 0;
for(i = 1; i <= n; ++i)
for(j = i + 1; j <= n; ++j)
res = max(res, s_down[i][j] + s_up[j][i]);

printf("%d", res - 2);

return 0;
}


显然状态已经没有办法再优化了。那么转移能否优化呢?

首先考虑能否优化到O(logn).

我们可以预处理出所有点关于先加入的点的极角序,利用树状数组,询问、更新均只需要O(logn)。

于是总的时间复杂度变为O(n^3logn).

然而,我们还能做的更好!

我们考虑换一个状态,比如将枚举y坐标最小的点,然后将合法的点按照关于它的极角序排序。

我们按照极角序进行dp.

将状态变为dp[i][j]表示i为最后一个加入的点,j为下一个尝试加入的点。

那么可以存在两种向下转移的方式:

若加入j,则dp[j][f(i,j)]=dp[i][j]+1,其中f(i,j)表示i指向j的射线逆时针旋转角度最近的某个点。

若不加入j,则dp[i][g(i,j)]=dp[i][j],其中g(i,j)所有点按照关于点i的极角序排序j的下一个点。

我们可以事先利用O(n^3)的复杂度完成f,g的预处理。

那么现在的状态O(n^3),转移O(1)。

于是总的时间复杂度为O(n^3),完美解决了此题。

代码还待填坑。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: