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

来源:互联网 发布:电影推荐知乎2015 编辑:程序博客网 时间:2024/05/23 07:24

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<=i<=k}

如果sum(yi){1<=i<=k}>=0 ,则说明  sigma(vi) / sigma(wi) >= x, 成立。


那么我们只要二分x {注意精度},就能找到单位重量价值最大的k个物品。 

#include<cstdio>#include<algorithm>using namespace std;const int maxn = 100000 + 10;const int INF = 1e7;int v[maxn], w[maxn];struct node {    double val;    int id;    bool operator < (const node& rhs) const {        return val > rhs.val;    }};node f[maxn];int ans[maxn];int n, k;//sigma(vi)/sigma(wi) >= x//-> sigma(vi - x*wi) >= 0int ok(double x) {    for(int i=0; i<n; ++i) {        f[i].val = v[i] - x*w[i];        f[i].id = i+1;    }    sort(f, f+n);    double sum = 0;    for(int i=0; i<k; ++i) {        sum += f[i].val;        ans[i] = f[i].id;    }    return sum >= 0;}int main() {    scanf("%d%d", &n, &k);    for(int i=0; i<n; ++i) {        scanf("%d%d", &v[i], &w[i]);    }    double l = 0, r = INF;    //for(int i=0; i<50; ++i) {    while(r-l>1e-8){        double mid = (l+r)/2;        if(ok(mid)) l = mid;        else r = mid;    }    for(int i=0; i<k; ++i) {        printf("%d", f[i].id);        if(i<k-1) printf(" ");        else printf("\n");    }    return 0;}



牛顿迭代

关于牛顿迭代法详见:点击打开链接


先取前k个元素算出S0 =∑(vi/wi) 作为初始值

然后对每一个元素(n个)求yi=vi-s0*wi

对yi从大到小排序,取前k个元素算出S,

重复上面的运算(每次循环后把S的值赋给S0,然后新一轮循环时S有通过S0计算出来),直到fabs(S-S0)<=eps,满足精度要求。


正确性证明:

证明其正确性,只要证明每次迭代的S都比上一次的大即可,也即迭代过程中S是单调递增的,因为给定的是有限集,故可以肯定,S必存在最大值,即该迭代过程是收敛的。下面证明单调性:

假设上轮得到的S1,则在n个元素中必存在k个元素使S1=∑(vi/wi),变形可得到∑vi-S1*∑wi=0,

现对每个元素求yi=vi-S1*wi,可知必存在k个元素使∑yi=∑vi-s1*∑wi=0, 所以当我们按y排序并取前k个元素作为求其∑y时,其∑y>=0,

然后对和式变形即可得到S1=((∑v-∑y)/∑w)<=(∑v/∑w)=s2,即此迭代过程是∑y是收敛的,当等号成立时,此S即为最大值。


#include<cstdio>#include<cmath>#include<algorithm>using namespace std;const int maxn = 100000 + 10;const double eps = 1e-8;int v[maxn], w[maxn];struct node {    double val;    int v, w;    int idx;    bool operator < (const node& rhs) const {        return val > rhs.val;    }};node f[maxn];int n, k;double Get() {    double sumv = 0, sumw = 0;    for(int i=0; i<k; ++i) {        sumv += f[i].v;        sumw += f[i].w;    }    return sumv/sumw;}int main() {    scanf("%d%d", &n, &k);    for(int i=0; i<n; ++i) {        scanf("%d%d", &f[i].v, &f[i].w);        f[i].idx = i+1;    }    double s1, s2 = Get();    do {        s1 = s2;        for(int i=0; i<n; ++i) {            f[i].val = f[i].v - s1*f[i].w;        }        sort(f, f+n);        s2 = Get();    } while(fabs(s2-s1)>=eps);    for(int i=0; i<k; ++i) {        printf("%d%c", f[i].idx, " \n"[i==n-1] );    }    return 0;}



0 0