2016级算法第六次上机-E.Bamboo之吃我一拳

Bamboo之吃我一拳

分析

当两个点的距离<=d时,才可以出拳,想要使得满足出拳条件的点对最少但不为0

寻找最近点对距离,得到的最近距离能够使得可以出拳的组数最少,因为除了最近点对外其他组合均不符合条件。

在一堆点中找到两个点的距离最小,暴力的O(n^2)计算量很恐怖,可以用分治思想把问题变小:
把平面上的点分为两拨,距离最近的两个点只可能出现在:第一堆,第二堆,和两堆2中各自一个点
分解
想象一条垂直线把所给点集分成两拨:所有的点要么在直线左边,要么在其右边。按x坐标升序排列。
解决
划分后两次递归调用,一次找到左边中的最近点对,一次找右边中的最近点对。取d‘为两者中的最小值
合并
最近点对的距离d要么是某次递归找到的d‘,要么是左边右边各取一个点组成的点对。就要查找是否存在距离小于d‘且一个在左一个在右的点对。如果这两个点的距离<d‘,那么两个点其实都各自在距离垂直线d‘的距离之内。也就是以垂直线为轴线,宽度为2*d‘的范围内,将这个范围内的点取出,并按照Y升序排列,只需要遍历任一点与范围内周围7个点的距离就可以了,比较选择更小值。

代码

#include <iostream>
#include <algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<iomanip>
using namespace std;

const int maxx = 1e5 + 3;
int num[maxx];
struct point{
    int x, y;
}p[maxx];
bool cmpx(const point&a, const point& b)//按x坐标排序
{
    return a.x<b.x;
}
bool cmpy(const int& a, const int &b)//按y坐标排序
{
    return p[a].y<p[b].y;
}
double dis(point a, point b)//计算两点距离
{
    return ((double)(a.x - b.x)*(a.x - b.x) + (double)(a.y - b.y)*(a.y - b.y));
}
double closest(int low, int high)//求最近距离
{
    if (low + 1 == high)//两个点情况,已经递归到底
        return dis(p[low], p[high]);
    if (low + 2 == high)//三个点
        return min(dis(p[low], p[high]), min(dis(p[low], p[low + 1]), dis(p[low + 1], p[high])));
    int mid = (low + high) / 2;
    double ans = min(closest(low, mid), closest(mid + 1, high));//两组分别递归,去最下者

    int i, j, cnt = 0;
    for (i = low; i <= high; i++)
    {
        if (p[i].x >= p[mid].x - ans && p[i].x <= p[mid].x + ans)
            num[cnt++] = i;
    }
    sort(num, num + cnt, cmpy);//这是在直线两侧ans距离内的点按y坐标排序
    for (i = 0; i < cnt; i++) {
        int k = i + 7 > cnt ? cnt : i + 7;
        for (j = i + 1; j < k; j++) {
            if (p[num[j]].y - p[num[i]].y > ans)
                break;
            ans = min(dis(p[num[i]], p[num[j]]), ans);
        }
    }
    return ans;
}
int main()
{
    int n;
    while (~scanf("%d", &n))
    {
        for (int i = 0; i<n; i++)
            scanf("%d %d", &p[i].x, &p[i].y);
        sort(p, p + n, cmpx);
        double ans = closest(0, n - 1);
        ans = sqrt(ans);
        printf("%.2lf\n", ans);
    }
}
另外

严格的说本题的数据是要卡掉O(n^2)暴力算法的,但你们的grh大佬借助sin 和cos计算距离,运算时间甚至比O(nlogn)的分治法还要快,,,

原文地址:https://www.cnblogs.com/AlvinZH/p/8185361.html

时间: 2024-10-28 19:09:19

2016级算法第六次上机-E.Bamboo之吃我一拳的相关文章

2016级算法第六次上机-A.Bamboo之寻找小金刚

Bamboo之寻找小金刚 分析 可以抽象为许多连续线段,分别计数左拐和右拐的个数.考察叉积的基础应用. 假设ABC三点构成一个夹角∠ABC,B就是拐点,AC是辅助形成夹角.考虑线段AB和BC形成的向量 sin∠ABC= (AB * BC)/|AB|*|BC| 两个向量的叉乘除以它们的模 所以叉乘可以判断夹角是否大于180°从而确定转向.当然叉积是有方向的,可以自己选择哪条边在前,只要标准统一即可.每三个点组成一组,遍历,分别计数左拐数和右拐数.具体叉积相关操作可以看<算法导论> 注意 常见的一

2016级算法第六次上机-C.AlvinZH的学霸养成记II

1032 AlvinZH的学霸养成记II 思路 中等题,贪心. 所有课程按照DDL的大小来排序. 维护一个当前时间curTime,初始为0. 遍历课程,curTime加上此课程持续时间d,如果这时curTime大于此课程DDL,表示无法学习此课程,但是我们不减去此课程,而是减去用时最长的那门课程(优先队列队首,课时最长). 贪心: 假设当前课程为B,被替换课程为A,则有A.d≥B.d,A.e≤B.e.既然curTime+A.d≤A.e,那么curTime+B.d≤B.e绝对成立,保证了B的合法性

2016级算法第四次上机-E.Bamboo and the Ancient Spell

Bamboo and the Ancient Spell 分析 可能英文读题难度比较大,但是只要看到全大写的 "THE LONGEST COMMON SUBSEQUENCE !"应该就清楚这是考什么的了. 最长公共子序列:可以不连续.序列长度很大时,暴力方法非常费时,这也是一道比较经典的<算法导论>上的动态规划题. 设序列X=和Y=的一个最长公共子序列Z=,则: 若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列: 若xm≠yn且zk≠xm ,则

2016级算法第三次上机-B.Bamboo和巧克力工厂

B Bamboo和巧克力工厂 分析 三条流水线的问题,依然是动态规划,但是涉及的切换种类比较多.比较易于拓展到n条流水线的方式是三层循环,外层是第k个机器手,里面两层代表可切换的流水线 核心dp语句:cost[i][k] = min(cost[i][k], cost[j][k-1]+t[j][i]+p[i][k]) 也可以在A题的基础上详细的列出所有可能的路线切割情况然后找到最小值. 注意本题与A题中t的含义不同. 上机时给出的伪代码 //数组从0开始 const int maxx= 510;

2016级算法第四次上机-B ModricWang的序列问题

1019 ModricWang的序列问题 思路 此题题意非常清晰,给定一个序列,求出最长上升子序列的长度.从数据规模来看,需要\(O(nlogn)\) 的算法. \(O(nlongn)\) 求最长上升子序列的做法如下: 维护一个数组\(f[]\) ,其中\(f[i]\) 表示当前步骤下长度为i的上升子序列的末尾元素的最小值. 需要注意的是,\(f[i]\) 一定是单调递增的,这个结论十分显然,这里就不做证明了. 使用动态规划思想,对于原序列中的每个元素,都拿去更新一次\(f[]\) .假设当前元

2016级算法第三次上机-G.Winter is coming

904 Winter is coming 思路 难题.首先简化问题, \(n\) 个0与 \(m\) 个1排成一列,连续的0不能超过x个,连续的1不能超过y个,求排列方法数. 显然会想到这是动态规划.最快想到的方法是 \(dp[i][j][x][y]\) 表示已经有i个北境兵j个野人参与排列,且末尾有x个连续北境士兵或y个连续野人士兵的方案数.这方法显然是正确的,但是光是 \(dp[200][200][10][10]\) 数组已经十分接近本题内存限制了,保证MLE.状态转移方法是大模拟,四层fo

2016级算法第三次上机-C.AlvinZH的奇幻猜想——三次方

905 AlvinZH的奇幻猜想--三次方 思路 中等题.题意简单,题目说得简单,把一个数分成多个立方数的和,问最小立方数个数. 脑子转得快的马上想到贪心,从最近的三次方数往下减,反正有1^3在最后撑着保证减完.不好意思这是错的,因为1,27,64,125...等立方数之间并不是倍数关系,不能构成贪心策略.举个反例:96=64+8+8+8+8=64+27+1+1+1+1+1,答案明显是5,而贪心会算到7. 既然不是贪心,那就是DP了,没毛病.先讲一下常规做法吧,是这样想的:相当于把一个数化成几份

2016级算法第三次上机-F.ModricWang的导弹防御系统

936 ModricWang的导弹防御系统 思路 题意即为:给出一个长度为n的序列,求出其最长不降子序列. 考虑比较平凡的DP做法: 令\(nums[i]\) 表示这个序列,\(f[x]\) 表示以第\(x\)个数为结尾的最长的不降子序列的长度,状态转移方程为: \[ f[i]=(\max{f[j]}+1) \;\;\;\;\;\;\; \mbox{when $nums[i]<=nums[j]$}\\] f中的最大值即为答案. 时间复杂度\(O(n^2)\),空间复杂度\(O(n)\) 当然也可

2016级算法第四次上机-G.ModricWang的序列问题 II

1021 ModricWang的序列问题II 思路 此题与上一题区别不是很大,只是增加了一个长度限制,当场通过的人数就少了很多. 大体解题过程与上一题相同.区别在于对\(f[]\) 的操作.没有长度限制的时候,\(f[]\) 的更新策略是立即更新.假设间隔为\(T\),现在由于需要考虑间隔,那么在处理第\(i\) 个元素的时候,就需要看到 前\(i -T\) 个元素生成的\(f[]\) ,而不能受到第\(i-T+1\) 到 \(i-1\) 个元素的干扰.因此,考虑如下操作:每次准备更新\(f[]