蓝桥杯 广场舞

题目描述

LQ 市的市民广场是一个多边形,广场上铺满了大理石的地板砖。

地板砖铺得方方正正,就像坐标轴纸一样。

以某四块砖相接的点为原点,地板砖的两条边为两个正方向,一块砖的边长为横纵坐标的单位长度,则所有横纵坐标都为整数的点都是四块砖的交点(如果在广场内)。

广场的砖单调无趣,却给跳广场舞的市民们提供了绝佳的参照物。每天傍晚,都会有大批市民前来跳舞。

舞者每次都会选一块完整的砖来跳舞,两个人不会选择同一块砖,如果一块砖在广场边上导致缺角或者边不完整,则没人会选这块砖。(广场形状的例子参考下图)

现在,告诉你广场的形状,请帮 LQ 市的市长计算一下,同一时刻最多有多少市民可以在广场跳舞。

输入描述

输入的第一行包含一个整数 nn,表示广场是 nn 边形的(因此有 nn 个顶点)。

接下来 nn 行,每行两个整数,依次表示 nn 边形每个顶点的坐标(也就是说广场边缘拐弯的地方都在砖的顶角上。数据保证广场是一个简单多边形。

其中,nn 不超过 1000,横纵坐标的绝对值均不超过 10^8108。

输出描述

输出一个整数,表示最多有多少市民可以在广场跳舞。

输入输出样例

示例

输入

5
3 3
6 4
4 1
1 -1
0 4

输出

7

样例说明

广场题干图中所示,一共有 7 块完整的地板砖,因此最多能有 7 位市民一起跳舞。

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

题意

LQ 市的市民广场是一个多边形,广场上铺满了大理石的地板砖。

地板砖铺得方方正正,就像坐标轴纸一样。

以某四块砖相接的点为原点,地板砖的两条边为两个正方向,一块砖的边长为横纵坐标的单位长度,则所有横纵坐标都为整数的点都是四块砖的交点(如果在广场内)。

广场的砖单调无趣,却给跳广场舞的市民们提供了绝佳的参照物。每天傍晚,都会有大批市民前来跳舞。

舞者每次都会选一块完整的砖来跳舞,两个人不会选择同一块砖,如果一块砖在广场边上导致缺角或者边不完整,则没人会选这块砖。(广场形状的例子参考下图)

现在,告诉你广场的形状,请帮 LQ 市的市长计算一下,同一时刻最多有多少市民可以在广场跳舞。

输入

输入的第一行包含一个整数 nn,表示广场是 nn 边形的(因此有 nn 个顶点)。

接下来 nn 行,每行两个整数,依次表示 nn 边形每个顶点的坐标(也就是说广场边缘拐弯的地方都在砖的顶角上。数据保证广场是一个简单多边形。

数据规模如下:

  1. 对于 30%的数据,nn 不超过 100100,横纵坐标的绝对值均不超过 100100。
  2. 对于 50%的数据,nn 不超过 10001000,横纵坐标的绝对值均不超过 10001000。
  3. 对于 100%的数据,nn 不超过 10001000,横纵坐标的绝对值均不超过 10^8108(一亿)。

输出

输出一个整数,表示最多有多少市民可以在广场跳舞。

分析

本题是一道明显的计算几何的题目,简单来说题意是:求所给的多边形(任意多边形)内部有多少个完整的小正方形。同时我们注意到,多边形的顶点是按顺序(顺时针顺序或者逆时针顺序)依次给出的,这样我们在后面构造线段和该多边形时较为方便,首尾依次连接即可。

读题之后,首先我们可以想到,要先找到这个多边形的边界坐标,即找到多边形最大最小的横纵坐标,就相当于根据最大最小横纵坐标构造一个大正方形框住这个多边形,我们在这个大正方形内进行求解即可。

我们在计算整个多边形内的小正方形数目时,首先可以想到:如果我们将整个多边形使用 若干条直线 x=ix=i,将其分成 宽为 1 的若干长条,然后我们分别计算每个长条中有多少小正方形,最后求和便是答案。

这时我们来思考怎么计算一个长条中有多少个小正方形:假设我们计算 x=i-1x=i−1 和 x=ix=i 所围成的长条。很明显,这个长条的上下边为原多边形的边。那么我们分别计算 x=i-1x=i−1 和 x=ix=i与下边交点的纵坐标,分别向上取整后取最大值 y_1y1​。然后再计算x=i-1x=i−1 和 x=ix=i与下边交点的纵坐标,分别向下取整后取最小值 y_2y2​。那么显而易见,小正方形的数量为 y_2-y_1y2​−y1​。如下图所示:

但是我们还有两个点需要注意:

  1. 下图中的多边形,在分成长条时,红色部分出现了一个长条中包含了多边形的几部分,且背分隔开,此时,我们就需要记录多边形与 x=ix=i 产生交点的每条边,并且记录对应的交点纵坐标,然后按纵坐标升序排列,相邻的两两组合,形成若干小长条即可。

  1. 可能有两条线 与 x = ix=i 这条线交于一点(i,y) 在排序的时候有可能导致这两条线的位置互换,从而得出错误答案。那么,我们记录线在 x = i-0.5x=i−0.5 处的纵坐标,这样纵坐标就不会重复了(纵坐标的值关系到线的位置)。

总体上,我们找到了一个时间复杂度为 O(n^2)O(n2) 的解,可以通过 50% 的数据。

解法

  1. 定义点、线结构体,录入点(输入为有序输入,顺时针或者逆时针),然后两点构成一条线,nn 个点 nn 条线。

  2. 算出最左、最右的横坐标值 minx,maxx

  3. 当 x = minx+1 :

(1)遍历每一条线,如果这条线与 x = minx+1 这条线有交点,就把交点处的纵坐标算出,并用一个数组 ans 记录这条线还有纵坐标.

(2)对 ans 数组进行排序,以记录的纵坐标的大小进行从小到大的排序.

(3)对于排完序的 ans 数组,下标为 0 对应的线和下标为 1 对应的线在 minx 到 minx+1 之间所构成的封闭的空间是属于区域内部的;下标为 1 与下标为 2 所构成的空间属于外部;2-3 是内部,以此类推。

(4)针对于 ans[0] 我们需要求出线在 x=minx 处的纵坐标(并向上取整) y11,x = minx+1 处的纵坐标为(向上取整)y12:y1 = max(y11,y12);。对于 ans[1],我们在求 x = minx 和 x = minx+1 处的纵坐标时向下取整得到 y21,y22:y2 = min(y21,y22);

if(y2-y1>0){sum += y2-y1;
}

  1. x= x+1;循环第三步。
#include<iostream>
#include<math.h>
#include<algorithm>using namespace std;struct Point {//点int x;int y;
}P[1010];
struct Line{//线:两个点构成Point a;Point b;
}L[1010];
struct I_Y{//线的下标编号,还有对应的纵坐标int index;double y;
}ans[1010];
int N,sum = 0;//点的总数,方块的总数
bool cmp (struct I_Y a,struct I_Y b)//纵坐标排序
{return a.y<b.y;
}
int max(int a,int b)
{return a>b?a:b;
}
int min(int a,int b){return a>b?b:a;
}
double F_y(struct Line l,double x)//求线l经过横坐标为x的点的纵坐标,简单的几何计算。
{double x1 = fabs(l.a.x-l.b.x);double x2 = fabs(x - l.a.x);double y = (l.b.y-l.a.y)*x2/x1 + l.a.y;return y;
}
void solve()
{int minx ,maxx,i,j,k,count,ax,bx;minx = P[0].x;maxx = P[0].x;for(i=0;i<N;i++){//寻找最小、大横坐标if(P[i].x>maxx){maxx = P[i].x;}if(P[i].x<minx){minx = P[i].x;}}for(i=minx+1;i<=maxx;i++){//计算每个小长条中的小正方形数量count = 0;for(j=0;j<N;j++){ax = L[j].a.x;bx = L[j].b.x;if((ax<=i-1&&bx>=i) || (ax>=i && bx <=i-1)){//当这条线和x = i有交点时,记录线编号和交点纵坐标ans[count].index = j;ans[count].y = F_y(L[j],(i+i-1)/2.0);count++;}}sort(ans,ans+count,cmp);//排序for(k=0;k<count/2;k++){//向上取整函数 double ceil(double x) 向下取整是double floor(double x) 在#include<math.h>中int y1 = max((int)ceil(F_y(L[ans[k*2].index],i)),(int)ceil(F_y(L[ans[k*2].index],i-1)));int y2 = min((int)floor(F_y(L[ans[k*2+1].index],i)),(int)floor(F_y(L[ans[k*2+1].index],i-1)));//    printf("y1 = %d ,y2 = %d\n",y1,y2);if(y2-y1>0){sum+=y2-y1;}}}cout<<sum<<endl;
}
int main()
{int x,y;cin>>N;for(int i=0;i<N;i++){cin>>x>>y;P[i].x = x;P[i].y = y;}L[0].a = P[0];//第一个点和最后一个点构成一条线L[0].b = P[N-1];for(int i=1;i<N;i++){L[i].a = P[i];//相邻两点构成一条线L[i].b = P[i-1];}solve();return 0;
}

百分解法

本题需要先将所有的点按横坐标离散成一个个竖条的区间,最多不超过O(N)条,每条区间中最多不超过O(N)个小的梯形,总共不超过O(N^2)个梯形,然后再分别计算每个梯形中包含的整方块数量。

由于每个梯形的左右边(最左和最右的横坐标)都是整数,而纵坐标都是有理数,可以表示成P/Q的形式,对于这种梯形中整块的数量可以使用辗转相除法来加速计算,在O(log (PQ))的时间内求解。这一用法较难,也是本题的重要考查点。

本题的实现需要比较细致,梯形的情况需要考虑清楚才不会漏情况。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;const int maxn=10000+10;
struct Tline
{bool flag;int x1,y1,x2,y2;long long a,b,c;
} L[maxn];
struct Tnode
{int x,y;
} P[maxn];
pair<double,int> rank[maxn];
int key[maxn];
int n;long long gcd(long long a,long long b)
{return !b?a:gcd(b,a%b);
}//计算sum(ceil((a*i+b)/c),0<=i<n)
long long calc(long long a,long long b,long long c,long long n)
{if (a<0) return calc(-a,b+a*(n-1),c,n);if (!n) return 0;long long res=0;if (a>=c) res+=n*(n-1)/2*(a/c);a%=c;if ((b<0?-b:b)>=c) res+=n*(b/c);b%=c;if (!a) return res;res+=calc(c,(a*n+b)%c,a,(a*n+b)/c);return res;
}long long floor(Tline &L,int xl,int xr)
{long long res=calc(L.a,L.b,L.c,xr+1);res-=calc(L.a,L.b,L.c,xl);return res;
}long long ceil(Tline &L,int xl,int xr)
{long long res=calc(L.a,L.b+L.c-1,L.c,xr+1);res-=calc(L.a,L.b+L.c-1,L.c,xl);return res;
}bool cmp(Tline &T0,Tline &T1,int x1,int x2)
{long long I1=((long long)T0.a*x1+T0.b)/T0.c;long long I2=((long long)T1.a*x2+T1.b)/T1.c;if (I1<=I2) return 1;if (I1-I2>1) return 0;int u=((long long)T0.a*x1+T0.b)%T0.c;int v=((long long)T1.a*x2+T1.b)%T1.c;return ((long long)u*T1.c-(long long)v*T0.c<0);
}long long work(Tline &L0,Tline &L1,int xl,int xr)
{    //除去左边floor<=ceil的情况if (cmp(L0,L1,xl+L0.flag,xl+!L1.flag)){int ll=xl-1,rr=xr+1;for (int mid;ll+1<rr;){mid=(ll+rr)/2;if (cmp(L0,L1,mid+L0.flag,mid+!L1.flag)) ll=mid;else rr=mid;}xl=rr;if (xl>xr) return 0;}//除去右边floor<=ceil的情况if (cmp(L0,L1,xr-!L0.flag,xr-L1.flag)){int ll=xl-1,rr=xr+1;for (int mid;ll+1<rr;){mid=(ll+rr)/2;if (cmp(L0,L1,mid-!L0.flag,mid-L1.flag)) rr=mid;else ll=mid;}xr=ll;if (xl>xr) return 0;}long long res=0;//计算上边界下方格点数if (L0.y1<L0.y2) res+=floor(L0,xl,xr-1);        //斜率>0else res+=floor(L0,xl+1,xr);    //斜率<0//计算下边界下方格点数if (L1.y1<L1.y2) res-=ceil(L1,xl+1,xr);else res-=ceil(L1,xl,xr-1);return res;
}void getline(Tline &T)
{long long a=T.y2-T.y1,b=T.x2-T.x1;long long c=gcd(a,b);a/=c;b/=c;if (b<0) b*=-1,a*=-1;T.a=a;T.c=b;T.b=b*T.y1-a*T.x1;T.flag=T.y1>T.y2;
}int main()
{
//    freopen("C.in","r",stdin);
//    freopen("C.out","w",stdout);scanf("%d",&n);for (int i=0;i<n;i++) {scanf("%d%d",&P[i].x,&P[i].y);key[i]=P[i].x;}for (int i=0;i<n;i++) {L[i].x1=P[i].x;L[i].y1=P[i].y;L[i].x2=P[(i+1)%n].x;L[i].y2=P[(i+1)%n].y;if (L[i].x1>L[i].x2) {swap(L[i].x1,L[i].x2);swap(L[i].y1,L[i].y2);}getline(L[i]);}long long res=0;sort(key,key+n);for (int i=0;i+1<n;i++)if (key[i]!=key[i+1]){int tot=0;for (int j=0;j<n;j++)if (L[j].x1<=key[i] && L[j].x2>=key[i+1])rank[tot++]=make_pair((double)(L[j].a*(double)(key[i]+key[i+1])/2.0+L[j].b)/L[j].c,j);sort(rank,rank+tot);if (tot&1) printf("-___-\n");int prev=res;for (int j=0;j<tot;j+=2){long long xx=res;res+=work(L[rank[j+1].second],L[rank[j].second],key[i],key[i+1]);//    if (key[i]==9)//    cout << res-xx << endl;}//    cout << key[i] << " " << key[i+1] << " " << res-prev << endl;}cout << res << endl;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1619004.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

试题 算法训练 瓷砖铺放

问题描述   有一长度为N(1<&#xff2e;<10)的地板&#xff0c;给定两种不同瓷砖&#xff1a;一种长度为1&#xff0c;另一种长度为2&#xff0c;数目不限。要将这个长度为N的地板铺满&#xff0c;一共有多少种不同的铺法&#xff1f;   例如&#xff0c;长度为4的地…

建材安装php源码,PHP响应式瓷砖大理石建材企业网站整站源码(自适应手机移动端) dedecms内核...

【温馨提示】源码包解压密码&#xff1a;www.youhutong.com 资源描述 PHP响应式瓷砖大理石建材企业网站整站源码(自适应手机移动端) dedecms内核 源码介绍&#xff1a; 采用织梦最新内核开发的模板&#xff0c;该模板企业通用、瓷砖、大理石、建材类企业都可使用。 响应式自适应…

(PC+WAP)织梦模板大理石瓷砖建材类网站

模板介绍&#xff1a; 织梦内核开发的模板&#xff0c;该模板属于大理石加工、瓷砖地板、建材类企业使用 响应式自适应各种移动设备&#xff0c;同一个后台&#xff0c;数据即时同步&#xff0c;简单适用&#xff01; 原创设计、手工书写DIVCSS&#xff0c; 完美兼容IE7、Firef…

小鑫与地板砖

小鑫与地板砖 Time Limit: 1000ms Memory limit: 65536K 有疑问&#xff1f;点这里^_^ 题目描述 小鑫家里有一个面积为n*m的矩形地面。他找到了一种特别好看的地板砖&#xff0c;有x块&#xff0c;每块变长为a&#xff0c;于是就像把这些地板砖铺到这个地面上。 他想了一个很…

开发过程中自己遇到的异常(六)

连接数据库失败&#xff1a; InternalError: (pymysql.err.InternalError) (1130, "Host xxx.xx.1.106 is not allowed to connect to this MySQL server") (Background on this error at: http://sqlalche.me/e/2j85) 解决方式&#xff1a; mysql> use mysql; …

苹果6外音没有了怎么办_苹果手机没有设置闹钟每天都在响怎么办

大家好&#xff0c;我是时代财富智能客服时间君。以上问题我来为你解答。 以苹果11为例&#xff0c;其系统版本为&#xff0c;苹果手机没有设置闹钟每天都在响&#xff0c;是因为设置了就寝模式。其解决方法如下&#xff1a; 1.首先&#xff0c;打开手机时钟。 2.点击就寝选项…

基于51单片机的闹钟系统

一、系统设计 本次闹钟系统使用的是STC89C52单片机作为主控芯片&#xff0c;通过八位数码管显示时间&#xff0c;通过DS1302定时模块设置定时&#xff0c;采集到的数据会上传到单片机中&#xff0c;单片机会对信号进行处理&#xff0c;处理后的数据会上传到LCD1602显示屏上进行…

如何在Apple Watch上设置闹钟和计时器

Your iPhone can be used as an alarm clock, a stopwatch, and a timer. However, If you have an Apple Watch, you don’t have to take out your phone to use any of these tools. Your watch has built-in apps that perform the same functions. 您的iPhone可用作闹钟&a…

用闹钟网在线闹钟怎么添加倒计时提醒?

很多人在日常工作时&#xff0c;为快捷提高工作效率&#xff0c;会为每项工作制定完成的时间&#xff0c;比如这一项工作需要3个小时完成、那一项工作需要1个小时完成&#xff0c;可是一忙碌起来可能起初添加定时提醒时间就忘记了。 使用闹钟网在线计时器办公工具可以快捷帮助…

Day Maker:iPhone闹钟充电器

是否有过这样的时候&#xff0c;有时躺在床上玩儿手机&#xff0c;玩儿着玩儿着没电了&#xff0c;但是又不想下床去拿充电器&#xff0c;太麻烦&#xff0c;要是有一个可以放在枕边的充电器该多好啊。福音终于来了&#xff0c;由设计师Michale Kritzer设计的充电器Day Maker就…

火箭闹钟+android,闹钟就要凶残的! -- 火箭闹钟 #Android #iPhone

闹钟们想把我们叫醒可谓是煞费苦心&#xff0c;虐心闹钟更是层出不穷&#xff0c;但是像火箭闹钟这种又虐心又漂亮的却不多见。最近这款曾经被每日最美推荐过一次的应用升级到了 2.0 版本&#xff0c;再次登上每日最美的火箭闹钟会为我们带来哪些新功能呢&#xff1f; 可能是最…

在iPhone或iPad上设置闹钟的两种最快方法

If you often create or toggle alarms on your iPhone or iPad, there are two quick ways to do it without having to hunt for the Clock app on your Home screen. Here’s how to use them. 如果您经常在iPhone或iPad上创建或切换闹钟&#xff0c;则有两种快速的方法可以…

怎样一次删除所有iPhone闹钟?

大家都设置几个iPhone闹钟呢&#xff1f;每个闹钟都有不同用途&#xff0c;有起床用的、赖床用的、还有假日补眠专用的&#xff0c;针对不同情况设置不同iPhone闹钟&#xff0c;结果不知不觉就设置了很多个&#xff01;是的时候整理闹钟了&#xff0c;今天小编就来跟大家分享一…

美图秀秀美化版

美图秀秀可以轻松美化数码照片&#xff0c;独有一键P图、神奇美容、边框场景、超炫闪图等强 大功能&#xff0c;还有每日更新的海量素材。国内很多人在使用这款免费图片处理软件&#xff0c;1分钟 就能上手&#xff0c;据说比PS要简单100倍(^.^)&#xff01;去除广告&#xff0…

Sping源码(七)— 后置处理器(自定义后置处理器)

上一篇中简单介绍了Spring中invokeBeanFactoryPostProcessors方法的执行流程&#xff0c;以及BFPP和BDRPP类的介绍&#xff0c;这篇文章我们来自定义实现一个类的后置处理器。 自定义PostProcessor 自定义PostProcessor的方式一共两种&#xff0c;都是根据invokeBeanFactoryPo…

openCV实战-系列教程8:直方图与均衡化(直方图定义/mask操作/均衡化原理/均衡化效果/自适应均衡化)、原理解析、源码解读

打印图像直接用这个函数&#xff1a; import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey()cv2.destroyAllWindows()1、直方图 1.1 基本定义…

EXCEL的VBA宏密码破解

在Excel 文档中使用AltF11可以打开查看宏代码。而部分VBA宏使用了密码保护&#xff0c;如下图&#xff1a; 在不知道密码的情况下则无法查看到宏代码。 对策 用Emeditor以二进制方式打开文件&#xff0c;搜索[43 4D 47]&#xff0c;对应字符为CMG&#xff0c;将找到CMG后的3…

EXCEL密码破解/破解工作表保护密码(详细图文教程)

EXCEL密码破解/破解工作表保护密码(详细图文教程) 网上有很多这个代码&#xff0c;但很多朋友并不太了解如何运用在此做了一些整理&#xff0c;希望对大家有所帮助&#xff01; 注&#xff1a;很多时候会因为忘记密码丢失重要EXCEL文件而烦恼&#xff0c;这份代码就能帮你找回&…

Excel宏(VBA)密码破解

最近在研究一个Excel宏&#xff0c;想查看VBA代码但是有密码&#xff0c;于是想着能不能移除密码。网上查找一番资料后进行了尝试。 一&#xff0c;准备工具 ExcelHex Editor Neo 二&#xff0c;开始实践 首先将.xlsm后缀名的文件改为.zip文件 然后双击zip文件(不用解压文件…

excel密码破解软件Excel Password Unlocker下载和使用技巧(亲测有效!)

Excel Password Unlocker 5.0 汉化版是专为恢复丢失的 Microsoft Excel 密码设计的一个易于使用的工具。每秒可尝试2万多个密码。 软件授权&#xff1a;免费软件 软件语言&#xff1a;简体中文 软件大小&#xff1a;1.5 MB 系统支持&#xff1a;Winxp / vi…