sduacm16级寒假训练 贪心二分

https://vjudge.net/contest/147628
password: acmlab2016

A - A HDU2037
【题意】
给你一组电视节目的开始结束时间,让你尽可能多的去看电视.
【思路】
贪心.
节目结束时间相同时,肯定选择更晚开始的更优.明白了这一点剩下的就是排序+选取了.
【Code】

#include <cstdio>
#include <algorithm>
using namespace std;
struct node
{
    int t1;
    int t2;
}a[105];
int cmp(node a,node b)
{
    if(a.t2==b.t2)  return a.t1>b.t1;
    return a.t2<b.t2;
}
int main()
{
    int n,i,j,k,t;
    while(scanf("%d",&n)&&n)
    {
        for(i=0;i<n;i++) scanf("%d %d",&a[i].t1,&a[i].t2);
        sort(a,a+n,cmp);
        for(i=1,t=a[0].t2,k=1;i<n;i++)
        {
            if(a[i].t1>=t)
            {
                t=a[i].t2;
                k++;
            }
        }
        printf("%d\n",k);
    }
    return 0;
}

B - B HDU1051
【题意】
给你一堆木棍,开始安装木棍有个setup time,如果下一个安装的木棍的l和w均小于现在这个的木棍,安装下一个木棍就不需要setup time.让你求最小的setup time.
【思路】
贪心.
先从l和w都比较大的那根木棍开始装,装完这个去装比他l和w都小一点的木棍,再装更小的…..
【Code】

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
struct node
{
    int l,w;
} a[5050];
bool cmp(node x,node y)
{
    if (x.l>y.l) return 1;
    else if (x.l==y.l) return x.w>y.w;
    return 0;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for (int i=0;i<n;i++) scanf("%d %d",&a[i].l,&a[i].w);
        sort(a,a+n,cmp);
        //for (int i=0;i<n;i++) printf("%d %d\n",a[i].l,a[i].w);
        int tot=n;
        for (int i=1;i<n;i++)
            for (int j=0;j<i;j++)
            if (a[j].l>=a[i].l&&a[j].w>=a[i].w){
                tot--;
                a[j].l=a[i].l;
                a[j].w=a[i].w;
                a[i].l=0;
                a[i].w=0;
                break;
            }
        printf("%d\n",tot);
    }
    return 0;
}

C - C HDU2141
【题意】
给你a,b,c三个序列,和一组数据x问是否存在Ai+Bj+Ck=X.
【思路】
一开始直接O(T*l*n*logm*s)的做法直接TLE了…(l,n,m,s分别是a,b,c,x序列的元素个数)
仔细想了想,l,n,m<=500,那么完全可以先生成a,b(其他亦可)两个序列的任意两个数的和,再在这个序列中去二分查找x-c[i].
时间复杂度降到了O(T*log(l*n)*m*s)
【Code】

#include<cstdio>
#include<algorithm>
using namespace std;
int l,n,m,x,tmp;
int a[505],b[505],c[505],ab[250010];
int main()
{
    int T=0;
    while (~scanf("%d %d %d",&l,&n,&m))
    {
        T++;
        for (int i=0;i<l;i++) scanf("%d",&a[i]);
        for (int i=0;i<n;i++) scanf("%d",&b[i]);
        for (int i=0;i<m;i++) scanf("%d",&c[i]);
        int tot=0;
        for (int i=0;i<l;i++)
            for (int j=0;j<n;j++)
                ab[tot++]=a[i]+b[j];
        sort(ab,ab+tot);
        scanf("%d",&x);
        printf("Case %d:\n",T);
        while (x--)
        {
            scanf("%d",&tmp);
            bool flag=false;
            for (int i=0;i<m;i++)
            {
                int opt=tmp-c[i];
                int l=0,r=tot-1,mid;
                while (l<=r)
                {
                    mid=(l+r)>>1;
                    if (ab[mid]==opt){
                        flag=true;
                        break;
                    }
                    if (ab[mid]<opt) l=mid+1;
                    else r=mid-1;
                }
                if (flag) break;
            }
            if (flag) printf("YES\n");
            else printf("NO\n");
        }
    }
}

D - D HDU4004
【题意】
给一个[0,L]的小河中间有n块石头,最多跳m次,求青蛙跳跃能力(单次所跳最大距离)至少为多少.
【思路】
最大值最小问题或最小值最大的问题,一般就是二分.
青蛙的跳跃能力越强需要的次数就越少,因此青蛙的跳跃能力有单调性,所以二分枚举青蛙的跳跃能力,在判断这个能力下是否能够跳过去,每次在这个能力下尽可能跳到远的石头上,最后看m次是否能跳到对岸。
【Code】

#include<cstdio>
#include<algorithm>
using namespace std;
int L,n,m;
int a[500050];
int ok(int mid)
{
    int cnt=1,tot=0;
    for (int i=1;i<=n;i++)
    {
        tot+=a[i];
        if (tot>mid) {
            tot=a[i];
            cnt++;
        }
    }
    if (cnt<=m&&tot<=mid) return true;
    return false;
}
int main()
{
    while(~scanf("%d %d %d",&L,&n,&m))
    {
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        a[++n]=L;a[0]=0;
        sort(a,a+n);
        int l=0,r=L,mid;
        for (int i=n;i>=1;i--)
        {
            a[i]-=a[i-1];
            l=max(l,a[i]);
        }
        while (l<r)
        {
            mid=(l+r)/2;
            if (ok(mid)) r=mid;
            else l=mid+1;
        }
        printf("%d\n",l);
    }
}

E - E POJ3617
【题意】
n头奶牛,每个奶牛用一个大写字母代表.给你一个初始序列,每次只能从头或尾取字母,求字典序最小的一种取的方案.
【思路】
贪心.每次取都判断头或尾是否相同,不相同取字典序最小的,相同则判断他们相邻的下一个字母是否相同…依次类推
/* 其实可以存成两个字符串,每次取的时候从这边的头取,从那边的尾也取一个,然后判断是否相同直接比较字符串就行 */
【Code】

#include <iostream>
#include<cstdio>
#include <string>
using namespace std;
int n;
char s[2010];
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)cin>>s[i];
    int l=0,r=n-1,cnt=0;
    while(l<=r)
    {
        bool flag=false;
        for(int i=0;i<=r-l; i++)
        {
            if (s[l+i]<s[r-i])
            {
                flag=true;
                cnt++;
                break;
            }else if (s[l+i]>s[r-i])
            {
                flag=false;
                cnt++;
                break;
            }
        }
        if(flag) printf("%c",s[l++]);
        else printf("%c",s[r--]);
        if(cnt%80==0) printf("\n");
    }
    printf("\n");
    return 0;
}

F - F POJ3273
【题意】
给你n个数据,让你把他分为m份,使每一份和的最大值最小.
【思路】
二分每份和的最大值,看是否能分成至多m份(显然能分成m份一定能分成m+1份m+1<=n)
【Code】

#include<cstdio>
int n,m;
int d[100010];
bool ok(int x)
{
    int tot=0,num=1;
    for (int i=0;i<n;i++)
    {
        if (tot+d[i]<=x)
        {
            tot+=d[i];
        }else{
            tot=d[i];
            num++;
        }
    }
    if (num<=m) return true;
    return false;
}
int main()
{
    int l=0,r=0,mid;
    scanf("%d%d",&n,&m);
    for (int i=0;i<n;i++)
    {
        scanf("%d",&d[i]);
        if (d[i]>l) l=d[i];
        r+=d[i];
    }
    while (l<=r)
    {
        mid=(l+r)/2;
        if (ok(mid)) r=mid-1;
          else l=mid+1;
    }
    printf("%d\n",mid);
    return 0;
}

G - G POJ3111
【题意】
这里写图片描述本题即让我们从n件物品中选出k个使这个式子值最大.
【思路】
容易想到去二分s,看是否能找到符合的物品.但具体怎么做一开始还是没思路…看了题解知道使用01分数规划(但我之前也不知道这个东西).
下面介绍一下:

http://blog.csdn.net/hhaile/article/details/8883652
01分数规划问题:所谓的01分数规划问题就是指这样的一类问题,给定两个数组,a[i]表示选取i的收益,b[i]表示选取i的代价。如果选取i,定义x[i]=1否则x[i]=0。每一个物品只有选或者不选两种方案,求一个选择方案使得R=sigma(a[i]*x[i])/sigma(b[i]*x[i])取得最值,即所有选择物品的总收益/总代价的值最大或是最小。
数学分析中一个很重要的方法就是分析目标式,这样我们来看目标式。
R=sigma(a[i]*x[i])/sigma(b[i]*x[i])
我们来分析一下他有什么性质可以给我们使用。
我们先定义一个函数F(L):=sigma(a[i]*x[i])-L*sigma(b[i]*x[i]),显然这只是对目标式的一个简单的变形。分离参数,得到F(L):=sigma((a[i]-L*b[i])*x[i])。这时我们就会发现,如果L已知的话,a[i]-L*b[i]就是已知的,当然x[i]是未知的。记d[i]=a[i]-L*b[i],那么F(L):=sigma(d[i]*x[i]),多么简洁的式子。我们就对这些东西下手了。
再次提醒一下,我们的目标是使R取到最大值。
我们来分析一下这个函数,它与目标式的关系非常的密切,L就是目标式中的R,最大化R也就是最大化L。
F的值是由两个变量共同决定的,即方案X和参数L。对于一个确定的参数L来说,方案的不同会导致对应的F值的不同,那么这些东西对我们有什么用呢?
假设我们已知在存在一个方案X使得F(L)>0,这能够证明什么?
F(L)=sigma(a[i]*x[i])-L*sigma(b[i]*x[i])>0即sigma(a[i]*x[i])/sigma(b[i]*x[i])>L也就是说,如果一个方案使得F(L)>0说明了这组方案可以得到一个比现在的L更优的一个L,既然有一个更优的解,那么为什么不用呢?
显然,d数组是随着L的增大而单调减的。也就是说,存在一个临界的L使得不存在一种方案,能够使F(L)>0. 我们猜想,这个时候的L就是我们要求的最优解。之后更大的L值则会造成无论任何一种方案,都会使F(L)<0.类似于上面的那个变形,我们知道,F(L)<0是没有意义的,因为这时候的L是不能够被取得的。当F(L)=0使,对应方案的R值恰好等于此时的L值。
根据这样的一段性质,很自然的就可以想到二分L值,然后验证是否存在一组解使得F(L)>0,有就移动下界,没有就移动上界。

上面的内容理解了,这道题就应该没什么问题了.

【Code】

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const double eps=1.0E-6;
int n,k;
struct node{
    int v,w;
}a[100010];
struct ppt{
    int id;
    double x;
}b[100010];
bool cmp(const ppt &u,const ppt &v)
{
    return u.x>v.x;
}
bool ok(double s)
{
    for (int i=0;i<n;i++)
    {
        b[i].id=i+1;
        b[i].x=a[i].v-s*a[i].w;
    }
    sort(b,b+n,cmp);
    double sum=0.0;
    for (int i=0;i<k;i++)
    {
        sum+=b[i].x;
    }
    return sum>=0.0;
}
int main()
{
    scanf("%d %d",&n,&k);
    for (int i=0;i<n;i++)
    {
        scanf("%d %d",&a[i].v,&a[i].w);
    }
    double l=0.0,r=0x3f3f3f3f,mid;
    while (r-l>eps)
    {
        mid=(l+r)/2;
        if (ok(mid)) l=mid;
        else r=mid;
    }
    for (int i=0;i<k-1;i++)
        printf("%d ",b[i].id);
    printf("%d\n",b[k-1].id);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值