贪心这种基本是靠猜猜的,思路要证明的话反而不好搞:
对于这道题,我们要做的就是“收缩”整个区间,同时从样例可以看出可能有内部的元素变成了新的左边与右边,不能只跟踪原来的两个maxmin元素。主要是我老是在犹豫怎么才能拿到不同转移状态下的最右边/左边的元素,跟在写动态规划似的这里的贪心思路是:一定存在一个分界点:使得左边的全部向右,右边的全部向左,来这样收束整个区间(很抽象吧,那肯定是先猜后面再来证明合理性)
假设在从左向右走已经有牛向左走,但是之后有牛向右走。首先这肯定没法减少右边,对于左边:肯定没法做到“穿透”:肯定不如你上一个走左边的,人家一开始位置都比你左你还不去追。
说人话就是第i个元素向左走了,后面所有元素对于右边界都没有贡献了,右边同理。这样一来,对于每一个分界点,我们可以在O(1)内完成查询左右的操作。具体实现就是:
for(int r = 1;r < n;r++)//第i个元素开始右移,排除全左全右的情况
{
int leftbound = min(cowp[0] + x,cowp[r] - x);
int rightbound = max(cowp[r - 1] + x,cowp[n - 1] - x);
if(rightbound - leftbound < ans)
{
ans = rightbound - leftbound;
}
}
对应最左边和最右边的元素要么是原来的,要么是另一个区间的第一个。当然也有可能就整体全部向一个方向动,为了防止负数索引问题,需要提前排除。赋值给ans当初始值就OK了
AC代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin>>n;
vector<int> cowp(n,0);
int maxp = LLONG_MIN;
int minp = LLONG_MAX;
for(int r = 0;r < n;r++)
{
cin>>cowp[r];
}
sort(cowp.begin(),cowp.end());
int x;
cin>>x;
int ans = (*max_element(cowp.begin(),cowp.end()) - *min_element(cowp.begin(),cowp.end()));
for(int r = 1;r < n;r++)//第i个元素开始右移,排除全左全右的情况
{
int leftbound = min(cowp[0] + x,cowp[r] - x);
int rightbound = max(cowp[r - 1] + x,cowp[n - 1] - x);
if(rightbound - leftbound < ans)
{
ans = rightbound - leftbound;
}
}
cout<<ans<<endl;
return 0;
}
感觉贪心题就是个猜猜思路的过程,合理性的证明是后面实现时候的事情。但是做题见得多肯定比少要好,只能这样想了