Roping the filed C++ 预处理坑点+dp

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值