POJ 3111 K Best &&NYOJ 914 (二分+ 贪心,最大化平均值)

链接:NYOJ:click here, POJ:click here

题意:(最大化平均值,挑战编程P143)

有n个物品的重量和价值分别是w[i]和v[i],从中选出K个物品使得单位重量的价值最大。(1<=k<=n<=10^41<=w[i],v[i]<=10^6)

一般想到的是按单位价值对物品排序,然后贪心选取,但是这个方法是错误的,比如对nyoj的例题来说,从大到小地进行选取,输入的结果是5/7=0.714对于有样例不满足。我们一般用二分搜索来做(其实这就是一个01分数规划)

我们定义:

条件 C(x) :=可以选k个物品使得单位重量的价值不小于x。

因此原问题转换成了求解满足条件C(x)的最大x。那么怎么判断C(x)是否满足?

变形:(sigma(v[i])/sigma(w[i]))>=x (i 属于我们选择的某个物品集合S)

进一步:sigma(v[i]-x*w[i])>=0

于是:条件满足等价于选最大的k个和不小于0.于是排序贪心选择可以判断,每次判断的复杂度是O(nlogn)。

参考代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn =1000011;
const int eps  =1e-6;
int n,k,i,j,wi[maxn],vi[maxn];
double y[maxn];             //vi-x*wi
bool cmp(double x)          //判断是否满足条件
{
    for(int i=0; i<n; i++)
    {
        y[i]=vi[i]-x*wi[i];
    }
    sort(y,y+n);
    double sum=0;
    for(int i=0; i<k; i++) //从大到小前k个数的和
    {
        sum+=y[n-i-1];
    }
    return sum>=0;
}
void solve()
{
    double left=0,right=maxn;//right的值其实可以传vi[i]/wi[i]的最大值,时间更少
    for(int i=0; i<100; i++)
    {
        double mid=(left+right)/2;
        if(cmp(mid)) left=mid;
        else right=mid;
    }
//    while(right-left>eps) //这种写法更精确
//    {
//        double mid=(right+left)/2;
//        if(cmp(mid)) left=mid;
//        else right=mid;
//    }
    printf("%.2f\n",right);
}
int main()
{
    // freopen("1.txt","r",stdin);
    //freopen("2.txt","w",stdout);
    while(~scanf("%d%d",&n,&k))
    {
        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&wi[i],&vi[i]);
        }
        solve();
    }
    return 0;
}

poj3111:一样的思路,只是求满足条件的物品编号,这里用一个结构体,定义一个标识。

参考代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn =100001;
int  n,k,i,j,wi[maxn],vi[maxn],ans[maxn];
struct str
{
    double t;
    int post;
    bool operator<(const str& post)const//重载运算符
    {
        return t>post.t;
    }
} y[maxn];
bool cmp(double x)//注意类型是浮点型,整形会传参出错
{
    for(int i=0; i<n; i++)
    {
        y[i].t=vi[i]-x*wi[i];
        y[i].post=i;
    }
    sort(y,y+n);
    double sum=0;
    for(int i=0; i<k; i++)//从大到小取出前k个元素
    {
        sum+=y[i].t;
        ans[i]=y[i].post;
    }
    return sum>=0;
}
int main()
{
    // freopen("1.txt","r",stdin);
    //freopen("2.txt","w",stdout);
    while(~scanf("%d%d",&n,&k))
    {
        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&vi[i],&wi[i]);
        }
        double l=0, r=maxn;
        while(r-l>1e-6)
        {
            double mid = (l+r)/2;
            if(cmp(mid)) l = mid;
            else r = mid;
        }
        for(int i=0; i<k; i++)
            if(i!=k-1)
                printf("%d ",ans[i]+1);
            else printf("%d\n",ans[i]+1);
    }
    return 0;
}
时间: 2024-10-11 17:05:24

POJ 3111 K Best &&NYOJ 914 (二分+ 贪心,最大化平均值)的相关文章

poj 3111 K Best 最大化平均值 二分思想

poj 3111 K Best 最大化平均值 二分思想 题目链接: http://poj.org/problem?id=3111 思路: 挑战程序竞赛书上讲的很好,下面的解释也基本来源于此书 设定条件C(x):=可以选择使得单位重量的价值不小于x 如何判定C(x)是否可行 假设选了某个物品的集合是S,那么单位重量的价值是:\[ \sum\limits_{i \in S} {v_i } /\sum\limits_{i \in S} {w_i } \] 因此就变成了判断是否存在S满足下面的条件:\[

poj 3111 K Best ,二分,牛顿迭代

poj 3111  K Best 有n个物品的重量和价值分别是wi和vi.从中选出k个物品使得单位重量的价值最大. 题解: 1.二分做法 2.牛顿迭代 效率比较: 二分做法: 转换成判断是否存在选取K个物品的集合S满足下面的条件: sigma(vi) / sigma(wi) >= x   {vi∈S, wi∈S} -->   simga(vi - x*wi) >= 0 这样我们对  yi= vi - x*wi {1<=i<=n}从大到小排序,计算sum(yi) {1<=

NYOJ 914 Yougth的最大化

Yougth的最大化 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 Yougth现在有n个物品的重量和价值分别是Wi和Vi,你能帮他从中选出k个物品使得单位重量的价值最大吗? 输入 有多组测试数据 每组测试数据第一行有两个数n和k,接下来一行有n个数Wi和Vi. (1<=k=n<=10000) (1<=Wi,Vi<=1000000) 输出 输出使得单位价值的最大值.(保留两位小数) 样例输入 3 2 2 2 5 3 2 1 样例输出 0.75 分析:

POJ 3111 K Best 二分 最大化平均值

1.题意:给一共N个物品,每个物品有重量W,价值V,要你选出K个出来,使得他们的平均单位重量的价值最高 2.分析:题意为最大化平均值问题,由于每个物品的重量不同所以无法直接按单位价值贪心,但是目标值有界且能判断与最后答案的大小关系,所以用二分来做 3.代码: 1 # include <iostream> 2 # include <cstdio> 3 # include <cmath> 4 # include <algorithm> 5 using names

poj 3111 K Best 参数搜索之牛顿迭代法

题意: 给n个元素,每个元素有两个属性(v,w),现在要从中选k个,使sum(v)/sum(k)最大. 分析: 参数搜索的入门题,牛顿迭代比二分快很多. 代码: //poj 3111 //sep9 #include <iostream> #include <algorithm> #include <cmath> using namespace std; const int maxN=100024; int n,k; double s0,s1; struct Node {

POJ 3111 K Best(最大化平均值)

题目链接:click here~~ [题目大意]有n个物品的重量和价值分别是Wi和Vi,从中选出K个物品使得单位重量的价值最大,输出物品的编号 [解题思路]:最大化平均值的经典.参见click here~~ 代码: //#include <bits/stdc++.h> #include <stdio.h> #include <math.h> #include <string.h> #include <iostream> #include <

【二分查找-最大化平均值】POJ2976 - Dropping Test

[题目大意] 给出n组ai和bi,去掉k个使得a的总和除以b的总和最大. [思路] 也就是取(n-k)个数,最大化平均值,见<挑战程序设计竞赛>P144,最后公式为c(x)=((ai-x*bi)从大到小排列的前(n-k)个的和不小于0) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath>

POJ 3111 K Best 贪心 二分

题目链接: http://poj.org/problem?id=3111 题目描述: 解题思路: 代码: 这是我错的代码......一会儿回来改啊......一会儿回来可能会很沮丧 #include <iostream> #include <cstdio> #include <string> #include <vector> #include <cstring> #include <iterator> #include <cm

POJ 3111 K Best(二分答案)

[题目链接] http://poj.org/problem?id=3111 [题目大意] 选取k个物品,最大化sum(ai)/sum(bi) [题解] 如果答案是x,那么有sigma(a)>=sigma(b*x) 至于选取,就可以根据a-b*x排序,贪心选取即可. 对于输出物品的id,因为在不断逼近结果的过程中,排序的结果也不断在调整 所以我们最后的得到的排序结果的前k个就是答案. [代码] #include <cstdio> #include <algorithm> usi