尺取 算法
题型 1
查找序列的连续元素的子序列的最小长度,其总和大于或等于 maxsum。
过程分为四步
1.初始化左右端点。
2.不断扩大右端点,直到不满足while(sum<=maxsum&&r<n)的条件 。
3.如果sum 小于 maxsum ,直接结束(上一个 while 结束时 肯定遍历到了右边界)。或者sum 大于 maxsum ,进行下一步。
4.不断地将左端点 往右撤,一步一步的判断减去后是否比summax小,如果大,sum 的和 也相应地减去 a[j++] ,逐渐回到初始状态。如果 在中间减得过程有 sum小于 summax 的情况,那就 不好意思 ,回退过程结束,进行往右扩展,就会一直这样扩展,判断,回退,再扩,直到 到了 右边界 的时候 ,退出,while(1)。
用了一个 while(1) break; 来不断 的 实现 求出 最短的 序列 ,上面 实现过程中 ,都会有一次 ans==min(r-1,ans) 的结果 更新 。
算法相当 精妙。
模板::
{
int l=0,r=0,ans=n+1;
while(1)
{
while(sum<=maxsum&&r<n)
sum=sum+a[r++];
if(sum<maxsum)
break;
ans=min(r-1,ans);
sum=sum-a[l++];
}
if(ans>n)
cout<<"no"<<endl;
cout<<ans<<endl;
return 0;
}
2.找出一串数字 的 最大 的 和,并找出起始下表和末尾下标。
也是尺取的思想 ,不断对 左端点 和右端点 进行 移动 ,达到最大 的 一个 子序列。首先有一个 maxsum 来记录现在状态的和,还需要一个 去前面探路的 sum ,
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=100005;
using namespace std;
typedef long long ll;
int a[maxn];
int main()
{
int t,n,i,start,end,summax,sum,kase=0,g=0;
cin>>t;
while(t--)
{
memset(a,0,sizeof(a));
sum=0;
summax=0;
cin>>n;
for(i=0;i<n;i++)
{
cin>>a[i];
}
summax=a[0];
start=0;
end=0;
int l=0;
for(i=0;i<n;i++)
{
sum=sum+a[i];
if(sum>summax)
{
summax=sum;
end=i;
l=start;
}
if(sum<0)
{
sum=0;
start=i+1;
}
}
if(g)
cout<<endl;
printf("Case %d:\n",++kase);
cout<<summax<<' '<<l+1<<' '<<end+1<<endl;
g=1;
}
return 0;
}
3.题面:
Aggressive cows
农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,...,xN (0 <= xi <= 1,000,000,000). 但是,John的C (2 <= C <= N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?
Input
有多组测试数据,以EOF结束。 第一行:空格分隔的两个整数N和C 第二行——第N+1行:分别指出了xi的位置
Output
每组测试数据输出一个整数,满足题意的最大的最小值,注意换行。
Sample Input
5 3
1
2
8
4
9
Sample Output
3
Hint
1位置放一头牛,4位置放一头牛,它们的差值为3;最后一头牛放在8或9位置都可以,和4位置的差值分别为4、5,和1位置的差值分别为7和8,不比3小,所以最大的最小值为3。
本题 是一个 二分答案 ,尺取 ,的题型。
代码:
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#include<string.h>
typedef long long ll;
const ll maxn=100005;
ll a[maxn];
ll n;
ll cownum;
bool check(ll x) //判断这个 mid是大了还是小了,小了不满足题意,大了 装不下牛
{
ll k=1;
ll i,niunow;
niunow=a[0];
for(i=1;i<n;i++)
if(a[i]-niunow>=x)
{
niunow=a[i];
k++;
if(k==cownum)
return true;
}
return false;
}
int main()
{
ll i;
while(scanf("%lld%lld",&n,&cownum)!=EOF) //cin cout 超时
{
ll mid;
memset(a,0,sizeof(a));
for(i=0;i<n;i++)
scanf("%lld",&a[i]);
// cin>>a[i]; //会超时
sort(a,a+n);
ll left=0,right=a[n-1]-a[0];
while(left<=right)
{
mid=(left+right)/2.0;
if(check(mid))
left=mid+1; // 往距离大的 答案去找
else
right=mid-1; // 往距离小的答案去找
}
printf("%lld\n",left-1);
//cout<<left-1<<endl; //用 cin cout 会超时
}
return 0;
}
什么是二分答案呢 ?
就是对答案进行一系列的查找,直到找到那个 最优的答案就可以,感觉和二分查找类似,大多数情况下用于求解满足某种条件的最大(最小)值。下面看个图
在判断牛是否能装下时,用了一个 贪心 算法,使更多的牛能装下。先从小的隔间里 装牛,可以的话,就把这个牛的隔间位置记录,然后再比较下一个牛与它之间的距离,如果 大于之前 mid 二分的距离,就能装,如果不行,就不能装,直到所给的牛,全装下(或者不能装下),再去二分。贪心。
就像这个题一样,两头牛之间的距离就是这个 代价一样,如果太小,放在一个隔间里面,就不满足条件,如果让它们距离够大(前提是要把指定的牛装满),也不满足条件,所以牛隔间之间距离不大也不小(可以装下所有的牛,也不至于两头牛在一个隔间里面),才可以满足条件,然后再求满足条件中任意两头牛之间最小距离的最大值。
首先先想,两牛之间的最大距离是什么,首先对牛的隔间序号排序,那么第一个隔板和最后一个隔板间的距离就是两头牛最大的距离,这样 从 最大的距离 开始去分区间 ,来做,比从1距离简单,就不用再去搜比最优解 更小的 距离了。
这时候就需要去遍历距离,从而得到那个最优解,如果暴力遍历,数据量过大,很容易超时,我们 就用 二分的方法来做,就像(二分查找逐个分一半,在一半中遍历,就不用全部遍历,达到优化的效果,大大缩短时间),我们就可以把答案二分,得到最优的答案。
是如何得到最优的答案,如果目前的距离可以装下所有的牛,就将距离拉长,如果不满足,就将距离缩短,我们 一开始 定义了 两头牛之间的最短距离和最长距离,我们 就可以分区间 ,在哪一个区间 满足,就在 哪个区间 继续分 区间,如果在分的时候,不满足条件了 ,就会 往回退(意思就是将距离变小一点),就这样,一直一直分,直到不能再分的时候,就停止继续二分,这样就 能得到 牛的最小距离的最大值。
上面 有个 尺取 ,排版 有点乱 ,抱歉,我在完善完善。
还在不断完善中。
如有错误 ,请指正,谢谢。