程序设计:蒜头君的数轴(考数论知识)
今天蒜头君拿到了一个数轴,上边有 n 个点,但是蒜头君嫌这根数轴不够优美,想要通过加一些点让它变优美,所谓优美是指考虑相邻两个点的距离,最多只有一对点的距离与其它的不同。蒜头君想知道,他最少需要加多少个点使这个数轴变优美。
输入格式
输入第一行为一个整数 n(1<=n<=10^5)表示数轴上的点数。第二行为 n 个不重复的整数 x1,x2,…,xn(-10^9 ≤xi≤10^9),表示这些点的坐标,点坐标乱序排列。
输出格式
输出一行,为一个整数,表示蒜头君最少需要加多少个点使这个数轴变优美。
样例输入
4
1 3 7 15
样例输出
1
思路:该题考察的是数论里边的最大公约数,自己也是从开始的摸不着头脑,一点一点解析题意。
首先分析题目,n个点都在数轴上,并且起初是乱序(想到这里就需要对这组整数排序),优美就是n个点分成了n-1个线段,这n-1个线段的长度最多有1个距离与其他不同,你要做的就是进行加点满足优美的条件,该题是问加了多少点,所以只需考虑加的点数即可。
先存入n个整数,求出n-1个距离(求出n-1个距离和,后边有用),然后我们通过删去其中一个距离(注意是依次删除每个距离,就是枚举)求其他n-2个距离的最大公因数gcd,同时也得求出sum(n-2个距离和),用sum除gcd就是你n-2个距离被均等的分成若干段,这符合最少加的点数,为什么是sum除gcd呢?因为你的n-2个距离d1,d2,d3,dn,他们最大的公因数是gcd,就是每个数都能被gcd整数,那么他们的和也是可以被gcd整数的。temp=sum/gcd,就是在n-2个距离上切割出来的若干个距离,temp-(n-2)就是你在此基础上多切割出来的线段,也就是我们所求出来的最少点数。这就是大致思路。
以下为注意事项:
1为了避免在求n-2个距离的gcd浪费时间,我们用gcd1[i]表示前i个数的最大公因数,gcd2[i]表示i后的最大公因数(包括i)
2接上求gcd两端可让gcd1[1]=dist[1],gcd2[n-1]=dist[n-2],为了后边的算n-2个距离他们的公因数,读者可自行演算这些步骤,理解能够更加透彻
3接下来就是枚举每一种情况,求出最小的加点数即可
可AC
# include<iostream>
# include<algorithm>
using namespace std;
int arr[100001],dist[100009],gcd1[100009],gcd2[100009],n,i;
int gcd(int a,int b)
{
int t;
while(b)
{
t=a%b;
a=b;
b=t;
}
return a;
}
int main()
{
int temp,Min=0x7fffffff;
long long sum=0;
cin>>n;
for(i=0;i<n;i++)
cin>>arr[i];
if(n<=3)
{
cout<<0<<endl;
return 0;
}
sort(arr,arr+n);
for(i=1;i<=n-1;i++)
{
dist[i]=arr[i]-arr[i-1];
sum+=dist[i];
}
gcd1[1]=dist[1];
for(i=2;i<=n-1;i++)
{
gcd1[i]=gcd(dist[i],gcd1[i-1]);
//cout<<gcd1[i];
}
gcd2[n-1]=dist[n-1];
for(i=n-2;i>=1;i--)
{
gcd2[i]=gcd(dist[i],gcd2[i+1]);
//cout<<gcd2[i];
}
//枚举情况
for(i=1;i<=n-1;i++)
{
if(i==1)
temp=(sum-dist[i])/gcd2[i+1];
else if(i==n-1)
temp=(sum-dist[i])/gcd1[i-1];
else
temp=(sum-dist[i])/gcd(gcd1[i-1],gcd2[i+1]);
Min=min(Min,temp-(n-2));
}
cout<<Min<<endl;
return 0;
}
来源:CSDN
作者:平凡先森
链接:https://blog.csdn.net/qq_43566782/article/details/103878810