F. Beer Marathon(二分+迭代+贪心)

在这里插入图片描述在这里插入图片描述在这里插入图片描述
这道题题意很好理解,就是不好写。
题意:给n个初始摊位的位置,然后移动这些摊位使得两两摊位之间的距离都是k;求最小的移动距离的和;
这道题我也是看了题解才明白的;
因为我们是不是最终都会移动到两两距离相等?
意思就是(我们假设开始点为a[0],n=4):
在这里插入图片描述
那么是不是最后都会移动到上面的这种样式;
然后我们是不是可以通过题目给出的每个坐标和上面的相应位置相减;
比如我假设起点是1,2,3,4(其中k是1),题目给出的初始坐标为0,1,2,5;
那么我们是不是可以0-1,1-2,2-3,5-4;结果就是-1,-1,-1,1;是不是;
然后我们可以发现这个问题;如果我们假设起点是1,那么是不是题目中给出的位置最终都要变成1,2,3,4这些位置上;
所以我就可以发现上面差值的绝对值的和是不是就是总的移动距离;因为这只是我假设的起点在1时的一种情况下的答案,但是我们要求这个和最小。其实可以假设起点在其他坐标处,但是最后是不是都会变成这种形式:
在这里插入图片描述
所以我们是不是可以二分起点?
对了,思路就来了,但是这个起点区间是多少呢?
这个题有点玄学,为什么因为我把二分区间开到1e12才能过,而且还用到了迭代法;
其实这个题的思路已经明白了,就是枚举这个起点,然后不断记录最小答案;
其实这个题有点像白书上面的最大化最小值一样(二分+贪心);但是这个题还有迭代。。。。涨知识了;
具体见代码:
AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k;
ll a[1000010];
ll sum(ll d){
    
    //这个函数就是用来计算初始值和最终值的差值的和,也就是最终移动的距离
	ll res=0;
	for(int i=0;i<n;i++){
    
    
		  res+=abs(a[i]-d);
		  d+=k;
	}
	return res;
}
int main(){
    
    
	cin>>n>>k;
	for(ll i=0;i<n;i++)cin>>a[i];
	sort(a,a+n);//题目输入并没有顺序,所以需要排序
	ll l=-1e12,r=1e12;//二分起点坐标
	ll t1,t2;
	ll ans=1e18;//记录答案
	for(ll i=0;i<100;i++){
    
    
		ll L=l+(r-l)/3,R=r-(r-l)/3;//这里为什么不用L=l,R=r?就是迭代的知识,只不过这点确实不好想
		  t1=sum(L);
		  t2=sum(R);
		  if(t1<t2){
    
    
		  	 r=R;
		  }else {
    
    
		     l=L;
		  }
		  ans=min(ans,min(t1,t2));
		  //cout<<t1<<t2<<endl;
	}
	cout<<ans<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44555205/article/details/104450507

相关文章