木子亚日乀  

poj-1604  http://poj.org/problem?id=1064

题意:N条绳子,将它们剪成k段相同长度绳子,长度最长是多少?保留两位小数

思路:二分答案,然后判断是否满足条件,如果满足条件就增加长度,否则减少长度,直到找到答案。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define INF 1<<29
using namespace std;
int n,k;
double l,r;
double L[10005];
int judge(double x)
{
    int num=0;
    for(int i=0;i<n;i++)
        num+=(int)(L[i]/x);
    return num>=k;
}
int main()
{
    scanf("%d%d",&n,&k);
    l=0;
    r=INF;
    for(int i=0;i<n;i++)
    scanf("%lf",&L[i]);
    for(int i=0;i<100;i++)
    {
        double mid=(l+r)/2;
        if(judge(mid))
            l=mid;
        else
            r=mid;
    }
    printf("%.2lf\n",floor(r*100)/100);
    return 0;
}

像这样,如果在求解最大化最小化问题中,能够比较简单的判断条件是否满足,那么使用二分搜索是很好的解决方法。

对于小数问题,上述判断结束条件,固定了循环次数,100次循环可以精确到1e30,基本是没有问题的,还有一种方法是设置成(r-l)>eps,eps为精确度,但是这样子如果eps太小会陷入死循环。

 

 

 

poj-2456 http://poj.org/problem?id=2456

题意:约翰有N个牛舍,有M头牛,牛之间会互相攻击,所以约翰想使牛与牛之间的距离尽量大,求最大值。

思路:先对牛舍进行排序,然后将第一头牛放在第一个牛舍,然后二分枚举牛与牛之间的距离,然后判断是否满足条件。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k;
int l,r;
int xx[100005];
int judge(int x)
{
    int last=0;
    for(int i=1;i<k;i++)
    {
        int crt=last+1;
        while(crt<n&&xx[crt]-xx[last]<x)
            crt++;
        if(crt==n)
            return 0;
        last=crt;
    }
    return 1;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
        scanf("%d",&xx[i]);
    sort(xx,xx+n);
    l=0;
    r=xx[n-1]-xx[0];   //最大距离是排序后第一个牛舍和最后一个牛舍之间的距离
    while(r-l>1)
    {
        int mid=(l+r)/2;
        if(judge(mid))
            l=mid;
        else
            r=mid;
    }
    printf("%d\n",l);
    return 0;
}

 

 

 

poj-3258 http://poj.org/problem?id=3258

题意:一条和宽L,河中有N个垫脚石,然后给出N个垫脚石的位置,然后两个河岸的位置分别是0和L,然后约翰可以移走M个垫脚石,求垫脚石之间最小距离的最大值。

思路:首先,N个垫脚石+两个河岸相当于N+2个垫脚石,然后对垫脚石位置从小到大排序。然后确定二分的范围,之后判断是否满足条件。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int L,N,M;
int vis[50005];
int solve(int x)
{
    int cnt=0;
    int t=0;
    int e=1;
    while(e<N)
    {
        if(vis[e]-vis[t]>=x)   //如果当前垫脚石与前一个垫脚石的距离大于等于x;更新垫脚石
        {
            t=e;  
            e++;
        }
        else    //如果相邻两个垫脚石的距离小于x,则移走当前垫脚石,移动的数量加一,更新垫脚石
        {
            e++;
            cnt++;
        }
    }
    if(cnt>M)   //如果移动的次数大于M说明最小值的最大值过大,要减小最小值的最大值
    return 0;
    else
        return 1;
}
int main()
{
    scanf("%d%d%d",&L,&N,&M);
    vis[0]=0;
    for(int i=1;i<=N;i++)
        scanf("%d",&vis[i]);
    vis[N+1]=L;
    N=N+2;
    sort(vis,vis+N);
    int l=0;
    int r=vis[N-1];
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(solve(mid))
            l=mid+1;
            else
                r=mid-1;
    }
    printf("%d\n",r);
    return 0;
}

 

 

 

poj-3273   http://poj.org/problem?id=3273

题意:农夫约翰每天都有一定的零花钱,然后他规定了一个“月”,然后将N天变成M月,然后求他在这些月中可以花的钱的最大值。

思路:不断的二分枚举所能花的最大值,然后判断能否将N天分成M段,并且不超过最大值。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N,M;
int vis[100005];
int solve(int x)
{
    int sum=0;
    int cnt=1;
    for(int i=0;i<N;i++)
    {
        if(sum+vis[i]<=x)
            sum+=vis[i];
        else
            sum=vis[i],cnt++;
    }
    if(cnt>M)
        return 1;
    else
        return 0;
}
int main()
{
    scanf("%d%d",&N,&M);
    int l=-1,r=0;
    for(int i=0;i<N;i++)
    {
        scanf("%d",&vis[i]);
        l=max(l,vis[i]);
        r+=vis[i];
    }
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(solve(mid))
            l=mid+1;
        else
            r=mid-1;
    }
    printf("%d\n",l);
    return 0;
}

  

 

poj-3104 晒衣服 http://poj.org/problem?id=310

题意:简是个聪明的女孩,他要晒N件衣服,每件衣服都有自己的含水量,每过一分钟,每件衣服的含水量会减少1,如果将衣服放在烘干机中,每分钟含水量减少看,问简晒玩这些衣服需要多久。

思路:这题和之前一场Atcoder的题目一样,当时没有做出来,然后也没有找到解那道题的博客,所以当时就没有补。刚好写这道题的时候,想起来那道题,之后把他补了,附上连接:http://abc063.contest.atcoder.jp/tasks/arc075_b然后写这道题的时候有了一点思路,但是没考虑全面,假设用了x分钟,如果含水量小于等于x的衣服则自然晒干,大于x的衣服就需要用烘干机了,但是我们首先将他的含水量减去x,如果需要用烘干机烘干的话,说明他每分钟可以减少k,因为我们之前先减去了x,所以他用烘干机相当于每分钟减少了k-1的含水量,这样对于时间的判断就很容易写了。用二分就可以了。注意用long long!

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
int n,k;
ll vis[100005];
ll sum;
int solve(ll T)
{
    ll t=T;
    for(int i=0;i<n;i++)
    {
        ll x=vis[i]-T;
        if(x>0)
            t-=ceil(x*1.0/(k-1));    //如果除不尽的话肯定还需要一分钟,向上取整就可以了
    }
    if(t>=0)
        return 1;
    else
        return 0;
}
int main()
{
    while(scanf("%lld",&n)!=EOF)
    {
        sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&vis[i]);
            sum+=vis[i];
        }
        sort(vis,vis+n);
        scanf("%d",&k);
        if(k==1)
            printf("%lld\n",vis[n-1]);
        else
        {
            ll l=1;
            ll r=vis[n-1];
            while(l<=r)
            {
                ll mid=(l+r)/2;
                if(solve(mid))
                   r=mid-1;
                else
                   l=mid+1;
            }
            printf("%lld\n",l);
        }
    }
    return 0;
}

  

 

poj-2976 http://poj.org/problem?id=2976

题意:期末,你有n场考试,每场考试你做对了b道题目中的a道,你可以扔掉k门科目,然后用剩下的科目成绩去计算总成绩,总成绩等于100*(所选科目答对的题目总和/所选科目的总题数),求最大成绩。

思路:设单位题目的正确率不小于x,则所选科目答对的题目总和/所选科目的总题数>=x,变形的∑(ai-x*bi)>=0;因此可以对ai-x*bi排序进行选取,这样就变成了使得前(n-k)项的和大于等于0

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 1005
int v[MAX];
int w[MAX];
double y[MAX];
int n,k;
int solve(double x)
{
    for(int i=0;i<n;i++)
        y[i]=v[i]-x*w[i];
    sort(y,y+n);
    double sum=0;
    for(int i=0;i<n-k;i++)
        sum+=y[n-i-1];
    return sum>=0;
}
int main()
{
    while(scanf("%d%d",&n,&k)!=EOF&&n)
    {
        for(int i=0;i<n;i++)
            scanf("%d",&v[i]);
        for(int i=0;i<n;i++)
            scanf("%d",&w[i]);
        double l=0;
        double r=1e10;
        for(int i=0;i<100;i++)
        {
            double mid=(l+r)/2;
            if(solve(mid))
                l=mid;
            else
                r=mid;
        }
        int ans=100*(l+0.005);
        printf("%d\n",ans);
    }
    return 0;
}

  

 

poj-3111 http://poj.org/problem?id=3111

题意:n件商品,编号1-n,每件有价格和重量,从中选出k件,为了使单位重量的价格最大,应如何选取,输出商品编号。

思路:和上题一样。多了保存原来的位置

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 100005
#define eps 1e-8
struct Node{
    int v;
    int w;
    int xx;
    double y;
    bool operator < (const Node &a)const{
        return y>a.y;
    }
}node[MAX];
int n,k;
int solve(double x)
{
    for(int i=0;i<n;i++)
        node[i].y=node[i].v-x*node[i].w;
    sort(node,node+n);
    double sum=0;
    for(int i=0;i<k;i++)
        sum+=node[i].y;
    return sum>=0;
}
int main()
{
    scanf("%d%d",&n,&k);
    int sum=0;
    for(int i=0;i<n;i++)
    {
        scanf("%d%d",&node[i].v,&node[i].w);
        node[i].xx=i+1;
        sum+=node[i].v;
    }
    double l=0;
    double r=sum;
    while(r-l>eps)
    {
        double mid=(l+r)/2;
        if(solve(mid))
            l=mid;
        else
            r=mid;
    }
    for(int i=0;i<k-1;i++)
        printf("%d ",node[i].xx);
    printf("%d\n",node[k-1].xx);
    return 0;
}

 

最近复习了一下二分,之前不是很熟练,然后复习之后也对二分有了更深的认识,然后把今天写的题总结一下。题目来源于《挑战程序设计竞赛》。

英语水平很烂,题意应该还算清晰吧。写的不好的地方,也希望大家能指出来,应该也没有人会看到。

poj-1604  http://poj.org/problem?id=1064

题意:N条绳子,将它们剪成k段相同长度绳子,长度最长是多少?保留两位小数

思路:二分答案,然后判断是否满足条件,如果满足条件就增加长度,否则减少长度,直到找到答案。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define INF 1<<29
using namespace std;
int n,k;
double l,r;
double L[10005];
int judge(double x)
{
    int num=0;
    for(int i=0;i<n;i++)
        num+=(int)(L[i]/x);
    return num>=k;
}
int main()
{
    scanf("%d%d",&n,&k);
    l=0;
    r=INF;
    for(int i=0;i<n;i++)
    scanf("%lf",&L[i]);
    for(int i=0;i<100;i++)
    {
        double mid=(l+r)/2;
        if(judge(mid))
            l=mid;
        else
            r=mid;
    }
    printf("%.2lf\n",floor(r*100)/100);
    return 0;
}

  像这样,如果在求解最大化最小化问题中,能够比较简单的判断条件是否满足,那么使用二分搜索是很好的解决方法。

对于小数问题,上述判断结束条件,固定了循环次数,100次循环可以精确到1e30,基本是没有问题的,还有一种方法是设置成(r-l)>eps,eps为精确度,但是这样子如果eps太小会陷入死循环。

 

 

 

poj-2456 http://poj.org/problem?id=2456

题意:约翰有N个牛舍,有M头牛,牛之间会互相攻击,所以约翰想使牛与牛之间的距离尽量大,求最大值。

思路:先对牛舍进行排序,然后将第一头牛放在第一个牛舍,然后二分枚举牛与牛之间的距离,然后判断是否满足条件。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k;
int l,r;
int xx[100005];
int judge(int x)
{
    int last=0;
    for(int i=1;i<k;i++)
    {
        int crt=last+1;
        while(crt<n&&xx[crt]-xx[last]<x)
            crt++;
        if(crt==n)
            return 0;
        last=crt;
    }
    return 1;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
        scanf("%d",&xx[i]);
    sort(xx,xx+n);
    l=0;
    r=xx[n-1]-xx[0];   //最大距离是排序后第一个牛舍和最后一个牛舍之间的距离
    while(r-l>1)
    {
        int mid=(l+r)/2;
        if(judge(mid))
            l=mid;
        else
            r=mid;
    }
    printf("%d\n",l);
    return 0;
}

  

 

 

poj-3258 http://poj.org/problem?id=3258

题意:一条和宽L,河中有N个垫脚石,然后给出N个垫脚石的位置,然后两个河岸的位置分别是0和L,然后约翰可以移走M个垫脚石,求垫脚石之间最小距离的最大值。

思路:首先,N个垫脚石+两个河岸相当于N+2个垫脚石,然后对垫脚石位置从小到大排序。然后确定二分的范围,之后判断是否满足条件。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int L,N,M;
int vis[50005];
int solve(int x)
{
    int cnt=0;
    int t=0;
    int e=1;
    while(e<N)
    {
        if(vis[e]-vis[t]>=x)   //如果当前垫脚石与前一个垫脚石的距离大于等于x;更新垫脚石
        {
            t=e;  
            e++;
        }
        else    //如果相邻两个垫脚石的距离小于x,则移走当前垫脚石,移动的数量加一,更新垫脚石
        {
            e++;
            cnt++;
        }
    }
    if(cnt>M)   //如果移动的次数大于M说明最小值的最大值过大,要减小最小值的最大值
    return 0;
    else
        return 1;
}
int main()
{
    scanf("%d%d%d",&L,&N,&M);
    vis[0]=0;
    for(int i=1;i<=N;i++)
        scanf("%d",&vis[i]);
    vis[N+1]=L;
    N=N+2;
    sort(vis,vis+N);
    int l=0;
    int r=vis[N-1];
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(solve(mid))
            l=mid+1;
            else
                r=mid-1;
    }
    printf("%d\n",r);
    return 0;
}

  

 

poj-3273   http://poj.org/problem?id=3273

题意:农夫约翰每天都有一定的零花钱,然后他规定了一个“月”,然后将N天变成M月,然后求他在这些月中可以花的钱的最大值。

思路:不断的二分枚举所能花的最大值,然后判断能否将N天分成M段,并且不超过最大值。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N,M;
int vis[100005];
int solve(int x)
{
    int sum=0;
    int cnt=1;
    for(int i=0;i<N;i++)
    {
        if(sum+vis[i]<=x)
            sum+=vis[i];
        else
            sum=vis[i],cnt++;
    }
    if(cnt>M)
        return 1;
    else
        return 0;
}
int main()
{
    scanf("%d%d",&N,&M);
    int l=-1,r=0;
    for(int i=0;i<N;i++)
    {
        scanf("%d",&vis[i]);
        l=max(l,vis[i]);
        r+=vis[i];
    }
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(solve(mid))
            l=mid+1;
        else
            r=mid-1;
    }
    printf("%d\n",l);
    return 0;
}

  

 

poj-3104 晒衣服 http://poj.org/problem?id=310

题意:简是个聪明的女孩,他要晒N件衣服,每件衣服都有自己的含水量,每过一分钟,每件衣服的含水量会减少1,如果将衣服放在烘干机中,每分钟含水量减少看,问简晒玩这些衣服需要多久。

思路:这题和之前一场Atcoder的题目一样,当时没有做出来,然后也没有找到解那道题的博客,所以当时就没有补。刚好写这道题的时候,想起来那道题,之后把他补了,附上连接:http://abc063.contest.atcoder.jp/tasks/arc075_b然后写这道题的时候有了一点思路,但是没考虑全面,假设用了x分钟,如果含水量小于等于x的衣服则自然晒干,大于x的衣服就需要用烘干机了,但是我们首先将他的含水量减去x,如果需要用烘干机烘干的话,说明他每分钟可以减少k,因为我们之前先减去了x,所以他用烘干机相当于每分钟减少了k-1的含水量,这样对于时间的判断就很容易写了。用二分就可以了。注意用long long!

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
int n,k;
ll vis[100005];
ll sum;
int solve(ll T)
{
    ll t=T;
    for(int i=0;i<n;i++)
    {
        ll x=vis[i]-T;
        if(x>0)
            t-=ceil(x*1.0/(k-1));    //如果除不尽的话肯定还需要一分钟,向上取整就可以了
    }
    if(t>=0)
        return 1;
    else
        return 0;
}
int main()
{
    while(scanf("%lld",&n)!=EOF)
    {
        sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&vis[i]);
            sum+=vis[i];
        }
        sort(vis,vis+n);
        scanf("%d",&k);
        if(k==1)
            printf("%lld\n",vis[n-1]);
        else
        {
            ll l=1;
            ll r=vis[n-1];
            while(l<=r)
            {
                ll mid=(l+r)/2;
                if(solve(mid))
                   r=mid-1;
                else
                   l=mid+1;
            }
            printf("%lld\n",l);
        }
    }
    return 0;
}

  

 

poj-2976 http://poj.org/problem?id=2976

题意:期末,你有n场考试,每场考试你做对了b道题目中的a道,你可以扔掉k门科目,然后用剩下的科目成绩去计算总成绩,总成绩等于100*(所选科目答对的题目总和/所选科目的总题数),求最大成绩。

思路:设单位题目的正确率不小于x,则所选科目答对的题目总和/所选科目的总题数>=x,变形的∑(ai-x*bi)>=0;因此可以对ai-x*bi排序进行选取,这样就变成了使得前(n-k)项的和大于等于0

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 1005
int v[MAX];
int w[MAX];
double y[MAX];
int n,k;
int solve(double x)
{
    for(int i=0;i<n;i++)
        y[i]=v[i]-x*w[i];
    sort(y,y+n);
    double sum=0;
    for(int i=0;i<n-k;i++)
        sum+=y[n-i-1];
    return sum>=0;
}
int main()
{
    while(scanf("%d%d",&n,&k)!=EOF&&n)
    {
        for(int i=0;i<n;i++)
            scanf("%d",&v[i]);
        for(int i=0;i<n;i++)
            scanf("%d",&w[i]);
        double l=0;
        double r=1e10;
        for(int i=0;i<100;i++)
        {
            double mid=(l+r)/2;
            if(solve(mid))
                l=mid;
            else
                r=mid;
        }
        int ans=100*(l+0.005);
        printf("%d\n",ans);
    }
    return 0;
}

  

 

poj-3111 http://poj.org/problem?id=3111

题意:n件商品,编号1-n,每件有价格和重量,从中选出k件,为了使单位重量的价格最大,应如何选取,输出商品编号。

思路:和上题一样。多了保存原来的位置

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 100005
#define eps 1e-8
struct Node{
    int v;
    int w;
    int xx;
    double y;
    bool operator < (const Node &a)const{
        return y>a.y;
    }
}node[MAX];
int n,k;
int solve(double x)
{
    for(int i=0;i<n;i++)
        node[i].y=node[i].v-x*node[i].w;
    sort(node,node+n);
    double sum=0;
    for(int i=0;i<k;i++)
        sum+=node[i].y;
    return sum>=0;
}
int main()
{
    scanf("%d%d",&n,&k);
    int sum=0;
    for(int i=0;i<n;i++)
    {
        scanf("%d%d",&node[i].v,&node[i].w);
        node[i].xx=i+1;
        sum+=node[i].v;
    }
    double l=0;
    double r=sum;
    while(r-l>eps)
    {
        double mid=(l+r)/2;
        if(solve(mid))
            l=mid;
        else
            r=mid;
    }
    for(int i=0;i<k-1;i++)
        printf("%d ",node[i].xx);
    printf("%d\n",node[k-1].xx);
    return 0;
}
posted on 2017-09-29 19:02  木子亚日乀  阅读(167)  评论(0)    收藏  举报