acm基础训练1.26

L题:Dropping tests POJ-2976

  • 题意:给定n个a[i]和b[i],求在去除其中k组数的情况下,找到100*∑(ai)/∑bi的最大值
  • 题解:求分数和,这其实是一个01分数规划的样例题,为什么符合二分的模型也是因为可以拟合成一条曲线,所求答案成单调性,即为曲线的截距。具体操作见https://blog.csdn.net/hzoi_ztx/article/details/54898323
  • 代码
#include <iostream>
#include<algorithm>
#include<cstring>
 
using namespace std;
 
const int N=1e4+100;
 
int n,k;
int a[N],b[N];
double d[N];
 
double eps=1e-5;
 
bool check(double x)
{
    for(int i=1;i<=n;i++)
    {
        d[i]=a[i]-b[i]*x;
    }
    sort(d+1,d+1+n);
    double sumall=0;
    for(int i=k+1;i<=n;i++)
    {
        sumall+=d[i];
    }
    if(sumall>0)return true;
    else return false;
}
 
int main()
{
    while(cin>>n>>k&&n!=0)
    {
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        for(int i=1;i<=n;i++)
        {
            cin>>b[i];
        }
        double l=0,r=1.0;
        while(r-l>eps)
        {
            double mid=(l+r)/2;
            if(check(mid))
            {
                l=mid;
            }
            else r=mid;
        }
        cout<<(int)(l*100+0.5)<<endl;
    }
}

M题:K-Best POJ-3111

  • 题意:给定n个财宝,价值为vi重量为wi,求在拿k个财宝的基础上,使得s=∑vi/∑wi最大
  • 题解:还是个01分数规划的题啊,不同的是这个题需要保留所取的方案数,并且取的是最大的k个,开个结构体处理下就好,直接看代码吧
  • 代码:
#include <iostream>
#include<algorithm>
#include<cstring>
 
using namespace std;
 
const int N=1e5+100;
const double eps=1e-8;
 
int k,n;
 
struct node
{
    int v,w;
    int i;
}nodes[N];
 
struct node2
{
    double x;
    int i;
    bool operator<(const node2 &y)const
    {
        return x<y.x;
    }
}node2s[N];
 
 
bool check(double x)
{
    for(int i=1;i<=n;i++)
    {
        node2s[i].i=i;
        node2s[i].x=nodes[i].v-x*nodes[i].w;
    }
    sort(node2s+1,node2s+1+n);
    double sum=0;
    for(int i=n;i>=n-k+1;i--)
    {
        sum+=node2s[i].x;
    }
    if(sum>0)return true;
    else return false;
}
 
int main()
{
    cin>>n>>k;
    double l=0,r=1e7;
    for(int i=1;i<=n;i++)
    {
        cin>>nodes[i].v>>nodes[i].w;
        nodes[i].i=i;
    }
    while(r-l>eps)
    {
        double mid=(l+r)/2;
        if(check(mid))
        {
            l=mid;
        }
        else r=mid;
    }
    for(int i=n;i>=n-k+1;i--)
    {
        cout<<node2s[i].i<<' ';
    }
    cout<<endl;
}

N题:Median POJ-3579

  • 题意:n个数,∣Xi - Xj∣构成一个新的排序,求新的排序的中位数
  • 题解:
    中位数符合二分的一个规律,同时可以猜到新的中位数和原来序列的中位数存在关联。具体操作:将原来n个数排列,然后对最小数到最大数为l,r进行二分(代码里l取得0)然后check(mid)看一下有多少个比a[i]+mid要大的数,只有为m/2时,才为中位数
    小于等于二分之m的话,则r=mid
    大于二分之m的话,l=mid+1
    注意:1.这里卡了一个输入流,关闭scanf也不行,只能直接用scanf
    2.这种二分的模板下,最后答案应该是l-1,想了下,大概是中位数为(l+r)/2的时候会自动向左靠拢,这种情况下,最后答案取得是小于m/2而不是等于的值,+1就好了(不知道会不会被特判掉?感觉没啥问题)
    代码:
#include <iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define ll long long
 
using namespace std;
 
const int N=1e5+10;
int n;
int a[N];
ll m;
 
bool check(int x)
{
    ll cnt=0;
    for(int i=1;i<=n;i++)
        cnt+=n-(lower_bound(a+1,a+n+1,a[i]+x)-a-1);
    return cnt<=m/2;
}
 
int main()
{
    while(~scanf("%d",&n))
    {
        int l=0,r=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        m=n*(n-1)/2;
        sort(a+1,a+1+n);
        r=a[n];
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(check(mid))
            {
                r=mid;
            }
            else l=mid+1;
        }
        printf("%d\n",l-1);
    }
}

O题:Matrix POJ-3685

  • 题意:给定n,m n为n*n的方阵,其中每一个数的值为i2 + 100000 × i + j2 - 100000 × j + i × j,求方阵中的第m小的数
  • 题解:
    首先,想找第m个小数,对数的范围进行二分,然后判断是不是有m个数比其小,这个符合二分的定义
    然后在检验有多少个数比其小的过程中,可以发现该函数在j固定的时候,是关于i的增函数,于是循环遍历j,对于每一个j我们都可以进行二分查找i的值,看能生成多少个比当前查找的mid值要小的数,如果最终遍历后小于m,则返回false,反之返回true
    注意:
    这个题在写第二个二分的时候要注意到l=r的时候是可以进入二分条件中的,或者可以特判一下!
    代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
 
ll n,m;
int t;
 
ll fun(ll x,ll y)
{
    return x*x+100000*x+y*y-100000*y+x*y;
}
 
bool check(ll x)
{
    ll cnt=0;
    for(ll j=1;j<=n;j++)
    {
        ll l=1;
        ll r=n;
        ll ans=0;
        while(l<=r)
        {
            ll mid=(l+r)>>1;
            if(l==0&&r==0)
            {
                ans=0;
                break;
            }
            if(fun(mid,j)<=x)
            {
                ans=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        cnt+=ans;
    }
    return cnt>=m;
}
 
 
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        ll l=-1e5*n;
        ll r=n*n*3+1e5*n;
        while(l<r)
        {
            ll mid=(l+r)>>1;
            if(check(mid))
            {
                r=mid;
            }
            else {
                l=mid+1;
            }
        }
        cout<<l<<endl;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值