🐋🐋🐋竹鼠通讯(钻石;分治思想;模板题:就算几何平面点对问题)
时间限制:3秒
占用内存:128M
🐟题目描述
在真空中,一块无限平坦光滑绝缘不导热草地上有很多光滑且相同球形竹鼠,它们的坐标为(xi,yi)。竹鼠之间会通过脑电波联系彼此。现在请问相距最近两只竹鼠的直线距离分别是多少(所有竹鼠都在草地的第一象限)?
🐟输入输出格式
输入格式: 第一行一个整数n; 接下来 nn行每行两个非负浮点数,xi,yi,表示第 i个点的 X 坐标与 Y 坐标。xi,yi都精确到小数点后两位。 输出格式: 一行,一个浮点数,最短距离。精确到小数点后4位。
🐟样例
🐚样例1
输入: 4 0.0 0.0 0.0 1.0 1.0 0.0 1.0 1.0 输出: 1.0000
🐚备注
其中:0≤n≤10^5,竹鼠的坐标数据范围在int型范围内。并且可能会有重叠的竹鼠。
🐟题目思路
经典的最近点对问题,用分治思想解决。
先说每一轮的思想:
-
找到本轮的l、r、mid三条范围线,l是最左边的竹鼠所在的x位置,r是最右边的竹鼠所在的x位置,mid是l和r的中线
-
最小距离只会出现在三种情况中:两个竹鼠都在左边,两个竹鼠都在右边,两个竹鼠跨中线
-
分别求出两个竹鼠都在左边和都在右边的最小距离d,然后据此找出跨中线范围(mid±d)的竹鼠,将这些竹鼠按y排序,然后枚举任意两只竹鼠间的距离,找到最小距离即为结果
再说分治思想:
-
切割整个l、r范围直到最小划分单位(l和r要么差0要么差1):
-
l=r,距离设为无穷大
-
l=r-1,直接返回两只竹鼠的距离
-
感谢官网用户——那是松石,提供的思路。
🐟代码
#include <bits/stdc++.h>
using namespace std;
#define INF 10000000000
const int N=1e5+10;
struct point
{
double x, y;
}a[N];
int n,t[N];
double d=0;
bool cmp(point &a,point &b)//先按x排升序,x相等按y排升序
{
if(a.x<b.x) return true;
else if(a.x==b.x&&a.y<b.y) return true;
else return false;
}
bool cmp2(int &i,int &j)//只按y排升序
{
if(a[i].y<a[j].y) return true;
else return false;
}
double dis(int i,int j)//计算距离
{
double c=sqrt(pow(a[i].x-a[j].x,2)+pow(a[i].y-a[j].y,2));
return c;
}
double solve(int l,int r)//l和r是所有竹鼠所在的x范围
{
//分治中最小的两种任务情况:
if(l==r) return INF;//左右范围重叠
if(l==r-1) return dis(l,r);//左右范围没有重合,计算距离返回
//距离最近的两个竹鼠只会是:全在左边、全在右边、跨中线三种情况之一
int mid=(l+r)/2;//找到范围中线
//分别求全在左边和全在右边的各自最小距离
double d1=solve(l,mid);
double d2=solve(mid+1,r);
d=min(d1,d2);//得到全在左边和全在右边中的最小值
int k=0;//记录跨中线范围内的竹鼠的数量
//求跨中线的最小值
for(int i=l;i<=r;i++)
{
//对中线左右各d范围内的竹鼠按y排序,遍历得到这些竹鼠中的最小距离
if(fabs(a[i].x-a[mid].x)<=d)//fabs,返回浮点数的绝对值;记录范围内竹鼠的x坐标信息
{
t[k]=i;
k++;
}
sort(t,t+k,cmp2);//按y排序
for(int i=0;i<k;i++)//枚举这些竹鼠的两两配对情况,计算得到最小距离
{
for(int j=i+1;j<k&&a[t[j]].y-a[t[i]].y<d;j++)//从i+1开始,避免重复判断
{
d=min(d,dis(t[i],t[j]));
}
}
}
return d;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i].x>>a[i].y;
sort(a,a+n,cmp);
printf("%.4f",solve(0,n-1));
return 0;
}
🐋🐋🐋上色(星耀;递归分治)
时间限制:1秒
占用内存:128M
🐟题目描述
🐟输入输出格式
🐟样例
🐟题目思路
对每个区域有两种上色方式:竖着刷,需要l-r+1次;横着刷,次数需要计算。
如果横着刷,那就是刷到最短的那根的长度为止,剩下的部分继续判断是竖着刷还是横着刷。
不断横竖判断,最后结果就是横着刷和竖着刷的最小值。
递归的结束判断就是l>r了,也就是遍历完整个区域了。
感谢官方视频解析的思路。
🐟代码
#include <bits/stdc++.h>
using namespace std;
int n,m;
int h[5010];//记录还没刷的栅栏高度
int shu(int l,int r)//对l到r范围内的栅栏竖着刷,那就是栅栏的数量
{
return r-l+1;
}
int heng(int l,int r)//横着刷
{
int hmin=INT_MAX;
for(int i=l;i<=r;i++)//找到该范围内最矮的栅栏,底下范围全部横着刷
{
hmin=min(h[i],hmin);
}
for(int i=l;i<=r;i++)//还剩下的还没刷,更新高度
{
h[i]-=hmin;
}
int ans=hmin;
while(l<=r)
{
while(l<=r&&h[l]==0)//找到剩余还未刷的一个子区域的左边界l
{
l++;
}
int rr=l;//本段从l开始的连续区域的右边界的下一个位置
//※※※rr从l+1开始的话就超内存了,有没有懂的大佬帮忙解答下原因~
while(rr<=r&&h[rr]!=0)//找到本段区域的右边界的下一个位置
{
rr++;
}
//对本段区域进行递归调用,判断最小的次数
ans+=min(heng(l,rr-1),shu(l,rr-1));
l=rr;//到下一段连续区域
}
return ans;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>h[i];
cout<<min(heng(1,n),shu(1,n))<<endl;
return 0;
}
🐋🐋🐋斐波那契数列(钻石;斐波那契数列性质求解最大公约数)
时间限制:1秒
占用内存:128M
🐟题目思路
大家都知道斐波那契数列即:f(1)=1,f(0)=0,f(i)=f(i−1)+f(i−2)(i≥2),现在请帮小码哥计算gcd(f(n),f(m))的值。
🐟输入输出格式
输入格式: 第一行输入两个整数n,m(1≤n,m≤50000)。 输出格式: 输出一个整数代表gcd(f(n),f(m)) 结果对1000000取模。
🐟样例
输入: 3 6 输出: 2
🐟题目思路
感谢官网用户——Silver,提供的思路。
🌮补充知识:斐波那契数列性质
前置知识:
-
1:Fn和Fn+1互质
-
n=0时显然成立
-
n<=k-1时也成立:
-
假设Fk和Fk-1互质,根据定义,Fk+1=Fk+Fk-1,假设存在d>1可以同时整除Fk+1和Fk,可知d也可以整除两者之差Fk-1,这与Fk和Fk-1互质矛盾
-
-
-
2:Fn+m=FmFn+1+Fm-1Fn
-
使用数学归纳法证明
-
m=1时,Fn+1=F1Fn+1+F0Fn=Fn+1成立
-
假设m<=k时都成立:
-
那么当m=k+1时,我们有Fn+k+1=Fn+k+Fn+k-1=(套用假设)FkFn+1+Fk-1Fn+Fk-1Fn+1+Fk-1Fn=(合并同类项)(Fk+Fk-1)Fn+1+(Fk-1+Fk-2)Fn=(斐波那契数列定义)=Fk+1Fn+1+FkFn,得证
-
-
据此可知(证明)最大公约数特性:
-
根据前置知识2, 可以知道任何 Fn, Fm 的公约数, 都是 Fn+m的约数
-
根据前置知识1、2,可以知道任何 Fn+m, Fn 的公约数 d, 都是 Fm 的约数
-
根据以上两个结论, 我们知道 d 能整除 Fm, Fn 等价于 d 能整除 Fm+n, Fn
-
推广上面的结论, 我们可以知道 d 能整除 Fm, Fn 等价于 d 能整除 Fm+kn, Fn
-
注意到 m = m + kn (mod n), 我们用m替换 m+kn可以得到: d能整除 Fm % n, Fn等价于 d 能整除 Fm, Fn. 所以 gcd(Fm, Fn) = gcd(Fm%n, Fn). 这实际上就是欧几里得法求最大公约数的迭代过程, 迭代到最后可以得到gcd(Fm, Fn) = gcd(F0, Fgcd(m, n))
-
由于 F0 =0, 且 gcd(0, x) = x, 我们得到 gcd(Fm, Fn) = Fgcd(m, n)
这道题目我们用到的特性就是:gcd(Fm, Fn) = Fgcd(m, n)
来源:TAOCP 学习记录 (1) - 斐波那契数列的最大公约数 · 瞎扯
🐟代码
#include<bits/stdc++.h>
using namespace std;
int main( )
{
long long n,m;
cin>>n>>m;
long long a=0,b=1,c=1;//F[0]、F[1]、F[2]
long long x=__gcd(n,m);//调用标准库中的函数__gcd(int a,int b)来计算最大公约数
if(x==0) cout<<a<<endl;//表示n和m没有公约数,根据特征,结果就是F0
else if(x==1) cout<<b<<endl;//表示n和m的最大公约数是1,根据特征,结果就是F1
else//n和m的最大公约数是x,根据特征,结果就是Fx
{
while(x>=2)//计算Fx
{
c=(a+b)%(long long)1e6;
a=b;
b=c;
x--;
}
cout<<c<<endl;//输出Fx
}
return 0;
}
有问题我们随时评论区见~
⭐点赞收藏不迷路~