题目:
说明如何在O(n)时间内解决分数背包问题
常规算法:
先求avgi = vi/wi,按照avgi从大到小排序,再贪心选择,时间复杂度为O(nlgn)
改进:
更一般的情况,不需要排序,例如:如果a1, a2,a3是avgi 最大的三个物品,如果它们的总量和不大于W,我们是不需要知道它们间的相对顺序的。
O(n)算法:
选择一个物品作为主元,对所有物品进行Paitition操作。将avg 分为三个集合G = {ai: avgi > m}, Q = {ai: avgi = m}, P = {ai: avgi < m}。分别对G, Q, P中元素重量求和得SG, SQ, SP。
1. 如果W < SG, 则令物体的集合为O = G,对集合O递归进行上述过程。
2. 如果 SG<=W <= SG+SQ,则将集合G中的元素都放入包中,然后对Q重复1,2
3. 如果 SG+SQ < W, 将G,Q中元素放入包中。令物体集合O = P,总重W = W - SG - SQ。递归进行上述过程。
代码:
#include <iostream>
using namespace std;
#define N 10
//物品信息
struct node
{
int weight;
int value;
};
int Partition(node *A, int p, int r)
{
node x = A[r];
int i = p-1, j;
for(j = p; j < r; j++)
{
//划分的依据是avg = value / weight
double t1 = x.value * 1.0 / x.weight;
double t2 = A[j].value * 1.0 / A[j].weight;
if(t2 >= t1)
{
i++;
swap(A[i], A[j]);
}
}
swap(A[i+1], A[r]);
return i+1;
}
int Randomized_Partition(node *A, int p, int r)
{
//随机选择数组中一个数作为主元
int i = rand() % (r-p+1) + p;
swap(A[r], A[i]);
//划分
return Partition(A, p, r);
}
//i是从1开使计数的,不是从p开始
int Randomized_Select(node *A, int p, int r, int weight, int value)
{
if(p == r)
return A[p].value;
//以某个元素为主元,把数组分为两组,A[p..q-1] < A[q] < A[q+1..r],返回主元在整个数组中的位置
int q = Randomized_Partition(A, p, r);
//主元是整个数组中的第q个元素
int i, w = 0, v = 0;
//求SG
for(i = p; i < q; i++)
{
w = w + A[i].weight;
v = v + A[i].value;
}
if(w == weight)//正是所求的元素
return value;
//主元物品取一部分
else if(w < weight && w + A[q].weight >= weight)
return value + A[q].value * (weight-w) / A[q].weight;
else if(w > weight)//所求元素<主元,则在A[p..q-1]中继续寻找
return Randomized_Select(A, p, q-1, w, value);
else//所求元素>主元,则在A[q+1..r]中继续寻找
return Randomized_Select(A, q+1, r, weight - w - A[q].weight, value + v);
}
int main()
{
node A[N+1];
int i;
for(i = 1; i <= N; i++)
cin>>A[i].weight>>A[i].value;
return 0;
}