这类的最大化最小值或者最小化最大值的问题,通常用二分搜索法就能够很好解决,并且效率很高。
我们定义C(d)为安排牛的位置使得最近的两头牛的距离不小于d。
那么我们就可以将问题变成求满足C(d)的最大的d。这样二分法就能很快地解决了。但是首先需要用贪心的思想把数据处理一下。
对牛舍的位置x进行升序排序
把第一头牛放入x0的牛舍
如果第i头牛放入xj的话,第i+1头牛就要放入满足xj+d<=xk的最小的xk中。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=1e5+100;
using namespace std;
int a[maxn],n,m;
bool judge(int x)
{
int l,r,t,i;
l=1;r=1;t=1;
while(r<=n)
{
while(r<=n&&a[l]+x>a[r])
r++;
if(r>n) break;
t++;
l=r;
if(t==m) break;
}
if(t==m) return true;
else return false;
}
int main()
{
int i,j,num,sum,mid;
scanf("%d%d",&n,&m);
sum=0;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+n);
num=a[n]/(m-1);
int l=0,r=num;
while(r-l>1)
{
mid=(l+r)/2;
if(judge(mid))
l=mid;
else
r=mid;
}
printf("%d\n",l);
return 0;
}
题意为给定一个n个数组成的序列,划分为m个连续的区间,每个区间所有元素相加,得到m个和,m个和里面肯定有一个最大值,我们要求这个最大值尽可能的小。
用二分查找可以很好的解决这个问题。这类问题的框架为,找出下界left和上界right, while(left< right), 求出mid,看这个mid值是符合题意,继续二分。最后right即为答案。
本题中的下界为n个数中的最大值,因为这时候,是要划分为n个区间(即一个数一个区间),left是满足题意的n个区间和的最大值,上届为所有区间的和,因为这时候,是要划分为1个区间(所有的数都在一个区间里面), 1<=m<=n, 所以我们所要求的值肯定在 [left, right] 之间。对于每一个mid,遍历一遍n个数,看能划分为几个区间,如果划分的区间小于(或等于)给定的m,说明上界取大了, 那么 另 right=mid,否则另 left=mid+1.
第四题:
这道题首先应该想好思路,这道题其实是一个找限值的过程,比如说一个排列,我划分很多的区间,要求每一个区间都不能大于某个值,设这个值为m,那么这个值最小应该是多少,以例1为例,最小应为900,以例2为例,最小应该为100,为什么?因为你有可能将排列中某一个值划为一个区间,如果你限值小于900,那么900就进不了任何区间,划分就会出问题了
同样我们还可以得出m最大值应该是排列相加的总和(把整个排列划为一个区间)。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
int n,k;
int a[510];
long long tot;
int maxa;
void Init()
{
cin>>n>>k;
tot=0;
maxa=-1;
for(int i=0;i<n;i++)
{
cin>>a[i];
tot+=a[i];
maxa=max(a[i],maxa);
}
}
int need(long long num)
{
long long c=0;
int sum=1;
for(int i=0;i<n;i++)
if(c+a[i]<=num) c+=a[i];
else
{c=a[i];sum++;}
return sum;
}
void print(long long num)
{
int last[510];
long long done = 0;
memset(last, 0, sizeof(last));
int remain = k;
for(int i = n-1; i >= 0; i--)
{
if(done + a[i] > num || i+1 < remain)
{
last[i] = 1; remain--; done = a[i];
}
else
{
done += a[i];
}
}
for(int i = 0; i < n-1; i++)
{
printf("%d ", a[i]);
if(last[i])
printf("/ ");
}
printf("%d\n", a[n-1]);
}
void guess()
{
long long l,r,m;
l=maxa;r=tot;
while(l<r)
{
m=l+(r-l)/2;
if(need(m)<=k) r=m;
else
l=m+1;
}
print(l);
}
int main()
{
int T,t;
cin>>T;
t=T;
while(T--)
{
Init();
guess();
}
return 0;
}
第四题:
题意:两个连续素数a和b之间的区间称为非素数区间(包括后边的素数b)。
给你一个数N,求N所在非素数区间的长度。
如素数 23~29 之间的非素数区间为24 25 26 27 28 +素数29。非素数区间长度为6(5+1)
给你一个数25 则25所在的非素数区间长度就为6。
思路:
若N为素数,则输出长度为0
若N为合数,则找出相邻的两个素数,输出长度为两素数的差。
#include<iostream>
const int maxn=1299710;
using namespace std;
long prime[maxn],num,b[maxn];
int main()
{
for(long i=2;i<maxn;++i)
b[i]=0;
for(long i=2;i<maxn;++i)
for(long k=i*2;k<maxn;k+=i)
b[k]=1;
num=0;
for(long i=2;i<maxn;++i)
if(b[i]==0)
prime[num++]=i;
int isprime(long n);
long n;
while(cin>>n)
{
if(n==0)
break;
if(isprime(n))
{
cout<<0<<endl;
continue;
}
for(long i=0;i<num;++i)
{
if(prime[i]<n&&prime[i+1]>n)
cout<<(prime[i+1]-prime[i])<<endl;
}
}
return 0;
}
int isprime(long n)
{
long j;
if(n==2)
return 1;
if(n<=1||n%2==0)
return 0;
j=3;
while(j*j<=n)
{
if(n%j==0)
return 0;
j+=2;
}
return 1;
}