Roping the filed (预处理+dp)
题目大意
在一平面上,按顺序给定 n 个点(事实上给出的点可以按顺逆时针围成一个闭合的多边形),m 个半径为 r 的圆。要给 n 个点两两连线,要求最多能连多少条线;
连线限制:
连线不能穿过任意一个圆,也不能与圆相切;
连线不能在中途相交,同一个点可以连多条线;
相邻的两个点不能连线(第一个点和第 n 个点是相邻的)
示例
5 3 1
6 10
10 7
9 1
2 0
0 3
2 2
5 6
8 3
输出
1
原题链接
http://poj.org/problem?id=3178[原题链接]
解题过程(详细看代码和注释)
/*预处理+区间dq,首先判断第i个角和第j个角之间能否连线,能则dp[i][j]=1,否则为0
做好上面工作后,就可以开始dp,子结构i:j,最优值dpans[1][N]
转移公式是,在dpans[i][j]与dpans[i][k]+dpans[k][j]+dp[i][j]中找最大值,i<k<j */
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef struct Point {
double x, y;
};
int dp[155][155], dpans[155][155];//dp[i][j]记录i,j是否能连线,能为1,不能为0
int N, G, R;//dpans用于dp
Point PS[155], CS[155];//PS是多边形坐标的集合,CS是圆心坐标的集合
void INIT() {
cin >> N >> G >> R;
for (int i = 1; i <= N; i++) {
scanf("%lf %lf", &PS[i].x, &PS[i].y);
}
for (int i = 1;i <= G;i++)
scanf("%lf %lf", &CS[i].x, &CS[i].y);
}//init
bool check(Point p1, Point p2) {
double r = 1.0*R + (1e-6);//double类型可能存在一定误差,所以稍微扩大r
double sx = 1.0*p1.x;
double sy = 1.0*p1.y;
double ex = 1.0*p2.x;
double ey = 1.0*p2.y;
double C = sqrt(1.0*(sy - ey)*(sy - ey) + 1.0*(sx - ex)*(sx - ex));
for (int i = 1; i <= G; i++) {//用正余弦定理判断直线是否与圆相切或相交
double A = sqrt((CS[i].x - sx) * (CS[i].x - sx) + (CS[i].y - sy) * (CS[i].y - sy));
double B = sqrt((ex - CS[i].x) * (ex - CS[i].x) + (ey - CS[i].y) * (ey - CS[i].y));
double cosA = (C * C + B * B - A * A) / (2.0 * C * B);
double cosB = (A * A + C * C - B * B) / (2.0 * A * C);
if (cosB < 1e-3 || cosA < 1e-3) {//当存在钝角时,那么此时应该判断圆心到两顶点的距离是否小于r
if (A < r || B < r) return 0;
else continue;
}//当角不是钝角时,此时判断圆心到直线的距离是否小于r
double SinA = sqrt(1.0 - cosA * cosA);
double SinB = sqrt(1.0 - cosB * cosB);
double h = B * SinA;
double h1 = A * SinB;//太坑,用不同的边计算出来的高可能会有所差异,但是要去最小的,真恶心
if (h < r || h1 < r) return 0;
}
return 1;
}//计算每个圆心到直线距离是否满足要求
int main(int argc, char* argv[])
{
INIT();
memset(dp, 0, sizeof(dp));
memset(dpans, 0, sizeof(dpans));
for (int i = 1; i < N; i++)
for (int j = i + 2; j <= N; j++)
if (i != 1 || j != N)//i=1,j=N是特殊情况
dp[j][i] = dp[i][j] = check(PS[i], PS[j]);//检查i,j间能否连线
for (int r = 2;r <= N-1;r++)//dp,子结构i:j,最优值dpans[1][N],步长r
for (int i = 1; i <= N - r; i++) {
int j = i + r;
dpans[i][j] = dpans[i][j]+dp[i][j];
for (int k = i + 1; k < j; k++) {
int t = dpans[i][k] + dpans[k][j] + dp[i][j];
if (t > dpans[i][j]) dpans[i][j] = t;
}
}
printf("%d\n", dpans[1][N]);
return 0;
}