hdu6603 Azshara's deep sea (计算几何+区间DP)

2 篇文章 0 订阅
1 篇文章 0 订阅
hdu6603 Azshara’s deep sea(2019hdu多校第三场1001)
【题目描述】

传送带
汗!感觉读题时间比想题时间还长
大概意思是wood stick(可视为顶点)围成了一个battle field,players可以在这个围成的battle field中(含边界)活动,另外这个区域内还有一些圆,现在有个boss要选若干对player并且把他们放在顶点,并用直线把他们连在一起,这些连线要满足两个条件:1)不可以和区域内的圆相切或相交,2)两两之间不可以相交(但端点可以相同)3)一对play不可以在相邻的顶点上
求boss最多可以选出多少对player

【思考】

其实就是选若干对不相邻的顶点,不相交,不经过圆。
先维护出一个凸包,预处理每条线段是否不经过圆,对于一条线段check的时间是O(G)(G是圆的个数),这些时间复杂度都不高,所以重点在于选哪些线段使他们两两不相交,有种三角剖分的冲动(也是三角不能相交或交于一边),而且顶点数也不大,所以就用区间dp来解决
转移方程是
dp[i][j]=max{dp[i][j],dp[i][t]+dp[t][j]+rel[i][j]} (i<t<j)
rel是预处理的顶点ij连边是否不经过所有圆
注意t不等于i或j,否则会重复计算

这题计算几何的部分参考了另一篇博客
传送带
这篇博客处理区间dp似乎是把原来的区间延长来得到一个循环,不过还是没有太看懂膜一下大佬

【代码】
#include <iostream>
#include <string.h>
#include <queue>
#include <stdlib.h>
#include <stdio.h>
#include <stack>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN=4e2+10;
const double eps=1e-10;
const double pi=acos(-1.0);
const ll INF=0x3f3f3f3f3f3f3f3f;
int T;
int N,R,G;
int dp[MAXN][MAXN];
int rel[MAXN][MAXN];
inline double sqr(double x){
	return x*x;
}
inline int dcmp(double x){
	if(fabs(x)<eps) return 0;
	return x>0?1:-1;
}
struct Point{
	double x,y;
	Point(){x=0,y=0;}
	Point(double _x,double _y):x(_x),y(_y){}
	void input(){scanf("%lf%lf",&x,&y);}
	void output(){printf("%.2lf %.2lf\n",x,y);}
	bool operator ==(const Point &b)const{
		return (dcmp(x-b.x)==0&&dcmp(y-b.y)==0);
	}
	bool operator !=(const Point &b)const{
		return !(dcmp(x-b.x)==0&&dcmp(y-b.y)==0);
	}
	bool operator <(const Point &b)const{
		return (dcmp(x-b.x)==0?dcmp(y-b.y)<0:x<b.x);
	}
	Point operator +(const Point &b)const{
		return Point(x+b.x,y+b.y);
	} 
	Point operator -(const Point &b)const{
		return Point(x-b.x,y-b.y);
	} 
	double len2(){
		return sqr(x)+sqr(y);
	}
	double len(){
		sqrt(len2());
	}
}p[MAXN];
inline double cross(Point a,Point b){
	return a.x*b.y-a.y*b.x;
}
inline double dis(Point a,Point b){
	Point p=b-a;
	return p.len();
}
struct Line{
	Point s,e;
	Line(){}
	Line(Point _s,Point _e):s(_s),e(_e){}
	double length(){
		return dis(s,e);
	}
};
struct Circle{
	Point p;
	double r;
	Circle(){}
	Circle(Point _p,double _r):p(_p),r(_r){}
}c[MAXN];
double point_to_line(Point p,Line a){
	return fabs(cross(p-a.s,a.e-a.s)/a.length());
}
void swap(Point &a,Point &b){
	Point t=a;a=b;b=t;
}
int relation(Line a,Circle b){
	double p=point_to_line(b.p,a);
	if(dcmp(p-b.r)==0) return 1;
	return (dcmp(p-b.r)<0?2:0);
}
Point tmp[MAXN];
int convex_hull(Point *p,int n,Point *ch){
	int m=0;
	sort(p,p+n);
	for(int i=0;i<n;i++){
		while(m>1&&dcmp(cross(tmp[m-1]-tmp[m-2],p[i]-tmp[m-1]))<=0) m--;
		tmp[m++]=p[i];
	}
	int k=m;
	for(int i=n-2;i>=0;i--){
		while(m>k&&dcmp(cross(tmp[m-1]-tmp[m-2],p[i]-tmp[m-1]))<=0) m--;
		tmp[m++]=p[i];
	} 
	if(n>1) m--;
	for(int i=0;i<m;i++) ch[i]=tmp[i];
	return m;
}
int main(){
	scanf("%d",&T);
	while(T--){
		memset(dp,0,sizeof(dp));
		memset(rel,0,sizeof(rel));
		scanf("%d%d%d",&N,&G,&R);
		for(int i=0;i<N;i++){p[i].input();}
		int n=convex_hull(p,N,p);
		for(int i=0;i<G;i++){c[i].p.input();c[i].r=R;}
		for(int i=0;i<n;i++){
			for(int j=i+2;j<n-(i==0);j++){
				int flag=1;
				for(int k=0;k<G;k++){
					if(relation(Line(p[i],p[j]),Circle(c[k]))){
						flag=0;
						break;
					}
				}
				if(flag==1) rel[i][j]=rel[j][i]=1;
			}
		}
		for(int k=2;k<n;k++)
			for(int i=0;i<n-k;i++){
				int j=i+k;
				for(int t=i+1;t<j;t++)
					dp[i][j]=max(dp[i][j],dp[i][t]+dp[t][j]+rel[i][j]);
			}
		int ans=0;
		for(int i=0;i<n;i++)
			for(int j=i+2;j<n-(i==0);j++)
				ans=max(ans,dp[i][j]);
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值