題目:
說明如何在O(n)時間內解決部分背包問題
思考:
http://www.cnblogs.com/meteorgan/archive/2012/04/22/2459117.html
常規演算法:
先求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總元素儘可能多的放入包中,結束。
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 / weightdouble 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;//求SGfor(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;}