2017.08.11小结

这类的最大化最小值或者最小化最大值的问题,通常用二分搜索法就能够很好解决,并且效率很高。

我们定义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;

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值