P3829 [SHOI2012] 信用卡凸包 题解

2024-04-06 07:36

本文主要是介绍P3829 [SHOI2012] 信用卡凸包 题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

解题思路

不难的题,但是细节有亿点多……

观察三组样例不难发现,我们可以把所有的信用卡的圆角去掉,变成一个长 b − 2 ⋅ r b-2\cdot r b2r,宽 a − 2 ⋅ r a-2\cdot r a2r 的矩形。考虑将这个矩形的四个顶点加入一个点集中,然后求凸包,答案即为这个凸包的周长加上一个半径为 r r r 的 ⚪ 的周长。

那么怎么求出凸包周长呢?显然的,我们在求出所有点的坐标后跑一遍 Andrew 或者 Graham 即可。那么现在问题变为如何求出所有点的坐标。

因为题目中已经给出了每张信用卡中心的坐标,那么我们可以很轻松地得到每张信用卡旋转前矩形四个顶点的坐标(其实就是四分之一圆的圆心),那么现在来解决旋转的问题。

我们分别对于每张信用卡进行考虑。我们不妨将中心移到单位圆的圆心上,那么我们根据顶点到中心的 x x x y y y 坐标之差(注意一定是按照顶点减中心的顺序),通过 atan2 函数就可以方便的算出当前顶点移动后与 x x x 轴的夹角大小。那么旋转就相当于是给夹角加上了一个度数,我们加上这个度数后直接用 sin ⁡ \sin sin cos ⁡ \cos cos 的值计算新得到的顶点坐标即可。

注意事项

  • atan2 函数使用时应该把 y y y 放前面, x x x 放后面;
  • 对于所有点按极角排序后一定要去重,不然一个点可能被多次计算;
  • 判断两个点是否相等时要加入精度限制,不然去重等于没去;
  • π \pi π 的值尽量取大一点,不然误差还是挺大的,尤其在乘以一个大的 r r r 后。

AC 代码

似乎有点长?

#include<math.h>
#include<stdio.h>
#include<valarray>
#include<stdlib.h>
#include<algorithm>
#define eps 1e-9
#define N 40005
struct Point{double x,y;//向量初始化Point():x(0),y(0){}Point(double a,double b):x(a),y(b){}//向量加inline Point operator +(const Point &b) const {return (Point){x+b.x,y+b.y};}//向量减inline Point operator -(const Point &b) const {return (Point){x-b.x,y-b.y};}//向量乘一个数inline Point operator *(const double &b) const {return (Point){x*b,y*b};}//向量除一个数inline Point operator /(const double &b) const {return (Point){x/b,y/b};}//向量点乘inline double operator ^(const Point &b) const {return x*b.x+y*b.y;}//向量叉乘inline double operator *(const Point &b) const {return x*b.y-y*b.x;}inline bool operator ==(const Point &b) const {bool _1=fabs(x-b.x)<eps;bool _2=fabs(y-b.y)<eps;return _1&&_2;}
}a[N],sta[N];
inline double dis(double x1,double y1,double x2,double y2
){  double Ox=(x1-x2)*(x1-x2);double Oy=(y1-y2)*(y1-y2);return(double)sqrt(Ox+Oy);
}
inline double dis(const Point &A,const Point &B
){double x1=A.x,y1=A.y;double x2=B.x,y2=B.y;return dis(x1,y1,x2,y2);
}
inline bool cmp(Point A,Point B
){Point _1=A-a[1];Point _2=B-a[1];double d=_1*_2;if(d>0) return true;double d1=dis(a[0],A);double d2=dis(a[0],B);if(d==0&&d1<d2) return true;return false;
}
int m,n,tail;
double A,B,R;
double x,y,d;
inline void Revolve(double &NowX,double &NowY,const double &CentreX,const double &CentreY,const double &Angle
){double r=dis(NowX,NowY,CentreX,CentreY);double DeltaX=NowX-CentreX;double DeltaY=NowY-CentreY;double NowAngle=atan2(DeltaY,DeltaX);NowAngle+=Angle;double NowSin=sin(NowAngle);double NowCos=cos(NowAngle);NowX=CentreX+(r*NowCos);NowY=CentreY+(r*NowSin);
}
inline void Turn(const double &x,const double &y,const double &d
){double x1,x2,x3,x4;double y1,y2,y3,y4;x1=x+B*0.5-R,y1=y+A*0.5-R;x2=x-B*0.5+R,y2=y+A*0.5-R;x3=x-B*0.5+R,y3=y-A*0.5+R;x4=x+B*0.5-R,y4=y-A*0.5+R;Revolve(x1,y1,x,y,d);Revolve(x2,y2,x,y,d);Revolve(x3,y3,x,y,d);Revolve(x4,y4,x,y,d);a[++n]={x1,y1},a[++n]={x2,y2};a[++n]={x3,y3},a[++n]={x4,y4};
}
inline void init(){scanf("%d",&m);scanf("%lf",&A);scanf("%lf",&B);scanf("%lf",&R);for(int i=1;i<=m;++i){scanf("%lf",&x);scanf("%lf",&y);scanf("%lf",&d);Turn(x,y,d);}for(int i=2;i<=n;++i){if(a[i].y>a[1].y)continue;if(a[i].y==a[1].y&&a[i].x>=a[1].x)continue;std::swap(a[1],a[i]);}std::sort(a+2,a+1+n,cmp);n=std::unique(a+1,a+n+1)-a-1;tail=1;sta[tail]=a[1];
}
double C,ans;
inline void Andrew(){for(int i=2;i<=n;++i){while(tail>1){Point d1=sta[tail]-sta[tail-1];Point d2=a[i]-sta[tail];if((d1*d2)>=0) break;--tail;}sta[++tail]=a[i];}sta[++tail]=a[1];for(int i=1;i<tail;++i)C+=dis(sta[i],sta[i+1]);
}
const double Pi=3.1415926535898;
signed main(){init();Andrew();ans=C+(2.0*R*Pi);printf("%.2lf",ans);
}

这篇关于P3829 [SHOI2012] 信用卡凸包 题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/879369

相关文章

LeetCode11. 盛最多水的容器题解

LeetCode11. 盛最多水的容器题解 题目链接: https://leetcode.cn/problems/container-with-most-water 示例 思路 暴力解法 定住一个柱子不动,然后用其他柱子与其围住面积,取最大值。 代码如下: public int maxArea1(int[] height) {int n = height.length;int

LeetCode:经典题之141、142 题解及延伸

系列目录 88.合并两个有序数组 52.螺旋数组 567.字符串的排列 643.子数组最大平均数 150.逆波兰表达式 61.旋转链表 160.相交链表 83.删除排序链表中的重复元素 389.找不同 1491.去掉最低工资和最高工资后的工资平均值 896.单调序列 206.反转链表 92.反转链表II 141.环形链表 142.环型链表 目录 系列目录141. 环形链表常量因子 1

C语言 | Leetcode C语言题解之第188题买卖股票的最佳时机IV

题目: 题解: int maxProfit(int k, int* prices, int pricesSize) {int n = pricesSize;if (n == 0) {return 0;}k = fmin(k, n / 2);int buy[k + 1], sell[k + 1];memset(buy, 0, sizeof(buy));memset(sell, 0, size

6月21日训练 (东北林业大学)(个人题解)

前言:   这次训练是大一大二一起参加的训练,总体来说难度是有的,我和队友在比赛时间内就写出了四道题,之后陆陆续续又补了了三道题,还有一道题看了学长题解后感觉有点超出我的能力范围了,就留给以后的自己吧。话不多说,上正文。 正文:   Problem:A 幸运数字: #include <bits/stdc++.h>using namespace std;int sum,ans;in

LeetCode:经典题之389 题解与延伸

系列目录 88.合并两个有序数组 52.螺旋数组 567.字符串的排列 643.子数组最大平均数 150.逆波兰表达式 61.旋转链表 160.相交链表 83.删除排序链表中的重复元素 389.找不同 1491.去掉最低工资和最高工资后的工资平均值 896.单调序列 206.反转链表 92.反转链表II 141.环形链表 142.环型链表 目录 系列目录389.找不同哈希表

Google Code Jam 2014(附官方题解)

2014年Google编程挑战赛 Problem A. Magic Trick Confused? Read the quick-start guide. Small input 6 points You have solved this input set. Note: To advance to the next rounds, you will need to s

Codeforces April Fools Day Contest 2014(附官方题解)

Codeforces2014年愚人节的坑题。。。但还是感觉挺好玩的。。。 A. The Great Game time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Two teams mee

【华东南AWDP】第十七届全国大学生信息安全竞赛 CISCN 2024 创新实践能力赛区域赛 部分题解WP

前言:这次区域赛AWDP安恒作为支持,赛制风格遵循安恒,一小时check一次。室温35°在室内坐了8小时,午饭是藿香正气水拌冰水。这场总体下来中规中矩吧。 WEB-welcome-BREAK Ctrl+U拿到flag WEB-submit-BREAK 文件上传,简单绕过 绕过就两个,一个MIMA头,一个等号换php(短标签) WEB-submit-FIX 修两个点,一个是

C语言 | Leetcode C语言题解之第174题地下城游戏

题目: 题解: int calculateMinimumHP(int** dungeon, int dungeonSize, int* dungeonColSize) {int n = dungeonSize, m = dungeonColSize[0];int dp[n + 1][m + 1];memset(dp, 0x3f, sizeof(dp));dp[n][m - 1] = dp[

Python | Leetcode Python题解之第174题地下城游戏

题目: 题解: class Solution:def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:n, m = len(dungeon), len(dungeon[0])BIG = 10**9dp = [[BIG] * (m + 1) for _ in range(n + 1)]dp[n][m - 1] = dp[n