#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define int long long
signed main()
{
int N,C;
cin>>N>>C;
vector<int> arr(N,0);
for(int i=0;i<=N-1;i++)cin>>arr[i];
sort(arr.begin(),arr.end());
int ret = 0;
for(int i=1;i<=N-1;i++)
{
int A = arr[i];
auto x = lower_bound(arr.begin(),arr.begin()+i,A-C);
auto y = upper_bound(arr.begin(),arr.begin()+i,A-C);
ret+=(y-x);
}
cout<<ret;
return 0;
}
#include<algorithm>
#include<vector>
#include<iostream>
#include<cmath>
using namespace std;
#define int long long
signed main()
{
int m,n;
cin>>m>>n;
vector<int> schools(m,0);
for(int i=0;i<=m-1;i++) cin>>schools[i];
sort(schools.begin(),schools.end());
int ret = 0;
for(int i=1;i<=n;i++)
{
int score;
cin>>score;
if(score<schools[0]) ret += (schools[0]-score);
else if(score>schools[m-1]) ret += (score-schools[m-1]);
else
{
auto it = lower_bound(schools.begin(),schools.end(),score);
int school1 = *it;
int school2 = *(it-1);
if(fabs(score-school1)>fabs(score-school2)) ret+=fabs(score-school2);
else ret+=fabs(score-school1);
}
}
cout<<ret;
return 0;
}
算法题小诀窍:当发现类型错误时,#define int long long + int main 改为 signed main 可以解决大多数问题
5.洛谷---木材加工
如下图所示,以两根原木分别 11cm 和 21 cm 为例,每段切为5cm的话,即11cm的切出2段,21cm的切出4段,总共6段;如果每段切为4cm的话,可以切出7段,依旧满足条件,但不是最优解
解法1:枚举从 0 cm到 maxlen cm(最长的一根原木的长度)的所有情况,每次判断一遍能切出多少段木头;对于第 i 根木头,可以切出 a[i] / x 段木头,所以假如 c 代表总的切出来的木段数,那么 c += a[i] / x(x为当前切的每段长度)
解法2:二分答案
不难发现 x 与 c 成反比,当 x 为 maxlen 的时候只能切一段,为 1 时能且非常多段,这就是题目的二段性;如下图所示,c(切出来的段数)大于等于k时,说明 x 比较小落在了左边区间内;c 小于k时,说明 x 比较大落在了右边区间;当处于某个 x 值时,c 恰好处于 >=k 与 <k 的界限上,那么就是最后的ret,所以可以把题目转换为寻找区间右端点来解决
寻找区间右端点:left = mid and right = mid - 1,c >= k and c < k
与上题解题思路大致相同,假设 h 表示当前电锯高度,c 表示当伐木机高度为 x 时能切出来的厘米数,则 h 与 c 成反比;所以 c >= M 时,h 在左区间,c < M 时,h 在右区间;对 [0,maxlen] 区间的值进行二分查找,maxlen 为最长的树木长度
根据上题的模板,只要把caculator函数稍微修改一下,就能够ac掉这道题了
注:本题数据大小会比较大,因此需要 #define int long long + signed main
代码:
cpp复制代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define int long long
int caculator(vector<int>& a,int x)//统计厘米
{
int ret = 0;
for(int i=0;i<=a.size()-1;i++)
if(a[i]>x) ret+=(a[i]-x);
return ret;
}
signed main()
{
int n,k;
cin>>n>>k;
vector<int> forests(n,0);
for(int i=0;i<=n-1;i++)cin>>forests[i];
sort(forests.begin(),forests.end());
int left=0,right=forests[n-1];
while(left<right)
{
int mid = left+(right-left+1)/2;
if(caculator(forests,mid)>=k) left = mid;
else right = mid - 1;
}
cout<<left;
return 0;
}
把 x 设为最短跳跃距离,c 设为跳跃距离为 x 下移走的岩石数目;假设把所有石头都移走,那么就得从起点一步跳到终点,通过该极限情况很容易发现 c 与 x 成正比
难点解析:
本题的难点在于,如何求出在跳跃距离为 x 的情况下,移走的岩石数目
可以通过一个双指针的方式来解决,定义双指针 i 、j ,初始都指向起点位置;然后 j 开始向后移动,直到移动到 a[j] - a[i] >= x 的情况下,此时 j - i - 1 即为所需移除的石头数量;然后 i 不要一步步往后移动到 j 位置,直接移到 j 位置即可,因为 i 移动时 j 已经处于最优的情况了,i 不断移动以后只会间隔越来越小,距离 x 也越来越远;最后对整个岩石数组重复该操作,并把每次情况统计到 ret 变量,因为 x 是最短跳跃距离,要所有的相邻石头都满足这个 x
代码:
cpp复制代码
#include<iostream>
#include<vector>
#include<limits.h>
using namespace std;
#define int long long
int caculator(vector<int>& a,int x)
{
int ret = 0;
int i = 0,j = 0,n = a.size();
while(i<=n-1)
{
j = i;
while(j<=n-1&&a[j]-a[i]<x)j++;
ret += (j-i-1);
i = j;
}
return ret;
}
signed main()
{
int L,N,M;
cin>>L>>N>>M;
vector<int> stones(N+2,0);
stones[N+1] = L;
for(int i=1;i<=N;i++) cin>>stones[i];
//开始二分答案
int left = 0,right = L;
while(left<right)
{
int mid = left+(right-left+1)/2;
if(caculator(stones,mid)<=M) left = mid;
else right = mid - 1;
}
cout<<left;
return 0;
}
代码易错点:
当 i = j = 3时,j 遍历完以后都没有把4、5两块石头给统计进去,同时最小的距离也不为 x ,14 与 25 之间只距离了 11,所以肯定出现逻辑错误;但我们可以假设把最后一块石头也给移出,这样14 与 +∞ 距离就能够大于 x 了,同时移除数量也是不会影响结果的