【HDU5928 2016CCPC东北地区大学生程序设计竞赛 - 重现赛 G】【计算几何 凸包思想 枚举底点做DP】Birthday Gift 给定绳长最多围住多少个点

Birthday Gift

Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 118    Accepted Submission(s): 24


Problem Description
Both Mr. Frog and Wallice love running. Wallice missed Mr. Frog’s birthday recently,so he decided to give a belated birthday gift. He quickly found out an idea: he decided to run a closed curve to contain those meaningful buildings. Unfortunately, he only gets a little time left since he is going to attend an important press conference.

Wallice wants to know the maximal number of buildings can be contained in the closed curve. Note that his speed is 1.
 

Input
The first line contains only one integer T,which indicates the number of test cases.

For each test case, the first line contains an integer N ( 1N80 ), and a double t ( 0l5000 ) indicating the numbers of buildings Wallice cares about and the time he has.

In the following n lines, the i-th line contains two doubles   xi,yi(600xi,yi600 ) indicating the position of the buildings.
 

Output
For each test case,output one line “Case #x: ans’’,where x is the case number (starting from 1) following with ans indicating the maximum number of buildings Wallice can circled in in limited time.
 

Sample Input
  
  
2 4 4.1 0 0 0 1 1 0 1 1 4 3.5 0 0 0 1 1 0 1 1
 

Sample Output
  
  
Case #1: 4 Case #2: 3
Hint
For the second sample, Wallice does not have enough time to circle all the four buildings so he circles three of them instead. It is guaranteed that the answer would not change even if l changes up to 10^-5, and there would not be any 3 points on one line even if any point changes its position up to 10^-5.
 

Source

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n; double L;
struct P
{
	double x, y;
	bool operator < (const P b)const
	{
		return (x * b.y - y * b.x > 0); //按照斜率从小到大排序
	}
	P operator - (const P b)const
	{
		return {x - b.x, y - b.y };
	}
}p[100], q[100];
double K(double x) { return x*x; }
double DIS(P a)
{
	return sqrt(a.x*a.x + a.y*a.y);
}
double f[100][100];	//f[i][j]表示以st为底端点时,以i为逆时针方向上的最后一个点,恰好包住st~i范围内的j个点的最小绳长
int main()
{
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%lf", &n, &L);
		for (int i = 1; i <= n; ++i)scanf("%lf%lf", &p[i].x, &p[i].y);
		int ans = 1;
		for (int st = 1; st <= n; ++st)
		{
			int g = 0;
			for (int i = 1; i <= n; ++i)
			{
				if (i != st && p[i].y >= p[st].y)q[++g] = p[i] - p[st];
			}sort(q + 1, q + g + 1);

			MS(f, 127); double inf = f[0][0];
			for (int i = 1; i <= g; ++i)
			{
				f[i][2] = DIS(q[i]);
				for (int j = i + 1; j <= g; ++j)
				{
					double link = DIS(q[j] - q[i]); if (f[i][2] + link > L)continue;
					int cnt = 1;
					for (int k = i + 1; k < j; ++k)
					{
						//查看k是否能被i-j包住,其实这里应该是 <= ,但是因为不存在三点共线,所以可以用 <
						if (q[j] - q[i] < q[k] - q[i])++cnt;
					}
					//for (int k = 2; f[i][k] != inf; ++k)//另一种写法
					for (int k = 2; k <= i + 1 && k + cnt <= j + 1; ++k)
					{
						double tmp = f[i][k] + link;
						if(tmp <= L)gmin(f[j][k + cnt], tmp);
					}
				}
				for (int k = 2; k <= n; ++k)if (f[i][k] + DIS(q[i]) <= L)gmax(ans, k);
			}
		}
		printf("Case #%d: %d\n", casei, ans);
	}
	return 0;
}
/*
【trick&&吐槽】
这里需要注意的是,
1,从最后一个点回到st的距离不能记在f中,因为该距离是可能变小的。
2,不存在三点共线大大简化了问题,否则极角排序在斜率一致的情况下,还要按照距离从近到远做排序

【题意】
有n(80)个点,范围在[-600,600]
有长度为l(0~5000,double类型)的绳子
问你我们用这个绳子最多能围住多少个点(围成凸包围住)
数据保证不会存在三点共线

【类型】
计算几何

【分析】
这是计算几何,不过也在计算几何的基础上加了DP
我们可以枚举最低点st,按照关于st的极角排序(其实就是斜率排序啦,使得所有点都逆时针排列),存在q[]中
然后用f[i][j]表示以st为底端点时,以i为逆时针方向上的最后一个点,恰好包住st~i范围内的j个点的最小绳长

首先f[i][2]=DIS(q[i])
然后考虑转移
首先枚举转移前的节点i
然后枚举转移后的节点j
然后我们要看i到j的连线可以使得我们包含多少[i+1,j]之间的点,记做cnt

接下来就做转移
gmin(f[j][num + cnt],f[i][num] + DIS(q[i],q[j])
然后在f[i][k]+dis(q[i])<=L的条件下更新答案就好啦!

【时间复杂度&&优化】
O(n ^ 4)

*/


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值