CLRS 16.2贪心算法的原理

16.2-1
此处证明的思想是假设有一个最优解,然后可以由贪心算法得到此最优解。
S={1,2,...,n} 个商品,并已经按平均价值从高到底排序,即 viwivi+1wi+1 。令 A 是分数背包的一个最优解,并假定解里面也是按照平均价值从高到底排序。假设总重量和最优解的总价值分别是 W,V 。令 B 是从 S 中按照贪心算法求解得的解。
1) 总重量 Ww1 :显然装入第一个商品得到最优解;
2) 总重量 W>w1 :在 A 中前几个商品加起来为 w1 重的商品(这里的几个可能是一个,也可能是多个,还可能是某一个商品的其中一部分)。令 B=A{Aw1}+{w1} (即将 A 中的 w1 重的东西换成第一个商品的 w1 重量)。此时 A 的价值 {w1}+{} B 的价值是 {w1}+{} 。由于 w1 的价值肯定是小于等于 v1 (此商品全部装进去才有 v1 的价值)。因为 A 是最优解,所以 {w1}=v1 (否则换了之后价值肯定提高, A 就不是最优解,矛盾)。这样 A 的价值就和 B 的价值一样,而 A 是最优的,则 B 也是最优的。
然后递归求解问题,求解 S n1 个商品,包最大重量为 Ww1 。最后得到贪心算法的最优解。

附上分数背包的简单实现:

#include 
#include 
using std::cout;
using std::endl;

struct goods
{
    int weight;
    int value;
    goods():weight(0),value(0){}
};

bool cmp(goods a,goods b)
{
    return a.value/a.weight > b.value/b.weight;
}

int greedy(goods *array,int n,int weight)   //返回最终价值
{
    std::sort(array,array + n,cmp);
    int res = 0;
    for(int i = 0; i < n; i++)
    {
        if(weight >= array[i].weight)
        {
            res += array[i].value;
            weight -= array[i].weight;
        }
        else if(weight > 0)
        {
            res += weight * (array[i].value / array[i].weight);
            weight = 0;
            break;
        }
    }
    return res;
}

int main()
{
    int v[] = {60,100,120};
    int w[] = {10,20,30};
    goods ia[3];
    for(int i = 0; i < 3; i++)
    {
        ia[i].value = v[i];
        ia[i].weight = w[i];
    }
    cout << "Value is " << greedy(ia,3,50) << endl;
    return 0;
}

16.2-2
根据书上所说,0-1背包具有最优子结构性质,若商品 i 的重量大于剩余可装重量,我们将商品 i 从方案中删除,否则解决方案是剩余商品重量不超过 Wwi 的价值最高方案与不选商品 i 且剩余重量不超过 W 的价值最高方案的最大者。
c[i,w] 是商品 1,...,i 且重量是 w 的解决方案价值,则:

c[i,w]=0c[i1,w]max(vi+c[i1,wwi],c[i1,w])if i=0 or w=0if w_i>wif i>0 and w≥w_i

算法输入是最大重量 W n 个商品以及两个序列 v=[v1,v2,...,vn],w=[w1,w2,...wn]

DYNAMIC-0-1-KNAPSACK(v,w,n,W)
    let c[0..n, 0..W] be a new array
    for w = 0 to W
        c[0,w] = 0
    for i = 1 to n
        c[i,0] = 0
        for w = 1 to W
        if wi≤w
            if v_i + c[i-1,w-wi] > c[i-1,w]
                c[i,w] = v_i + c[i-1,w-wi] 
            else c[i,w] = c[i-1,w]
        else c[i,w] = c[i-1,w]

时间复杂度是 Θ(nW)

16.2-3
按照重量从低到高装入即可。设解决方案是 x=(x1,x2,...,xn) ,且 w1w2...wnv1v2...vn 。令 j 表示第一个没被装入的商品下标,所以 1..j1 的商品全部装入, j...n 的商品都没转入,现假设用 j...n 的某个商品替换 1...j1 中的某个商品,此时背包的价值下降且容量变小。

16.2-4
思想就是选择走最长的距离再补水,也就是说剩余的水不足以到达下一个补给点时就补水,否则就不补水。证明就略了。

16.2-5
算法如下:
1) 在点集中选取最小的点;
2) 取以该点为左起点的单位闭区间,然后从点集中去掉包含在该单位闭区间的所有点;
3) 重复1)-2),直到所有的点处理完毕;那么2)得到的单位闭区间集合A即为所求。

证明:首先,根据第二步肯定能得到一个解 A ,这个解包含所有的点。假设有一个最优解 B 。用以最小点为左起点的单位闭区间代替 B 中包含该最小点的单位闭区间, 得到的新的解不会比 B 更差,即 A 也是最优解。

int interval(double *x,int n)//返回区间个数
{
    bool *isVisit = new bool[n];
    for(int i = 0; i < n; ++i)
        isVisit[i] = false;
    int res = 0;
    for(int i = 0; i < n; ++i)
    {
        if(!isVisit[i])
        {
            isVisit[i] = true;
            ++res;
            double max = x[i] + 1;
            for(int j = i + 1; j < n; ++j)
            {
                if(x[j] > max)
                    break;
                else isVisit[j] = true;
            }
        }
    }
    return res;
}

16.2-6
先求每个商品的平均价值 avgi=vi/wi ,选择一个物品作为主元 m=avgm ,对所有物品进行Paitition操作,时间是 O(n) 。将平均价值 avg 分为三个集合 L={ai:avgi<m},E={aiavgi=m},G={ai:avgi>m} 。分别对 G,E,L 中元素求重量和得 SG,SE,SL
1. 如果 W<SG , 则令物体的集合为 O=G ,对集合 O 递归进行上述过程。
2. 如果 SGWSG+SE ,则将集合 G 中的元素都放入包中,并将集合 E 总元素尽可能多的放入包中,结束。
3. 如果 SG+SE<W , 将 G,E 中元素放入包中。令物体集合 O=L ,总重 W=WSGSE 。递归进行上述过程。
每次递归都花费线性时间,子问题变成原来的一般规模,运行时间为 T(n)T(n/2)+Θ(n) ,得到 T(n)=O(n)

16.2-7
集合 A,B 按照非递增的顺序排序即可。
证明:对于任意的下标 i<j ,考虑 abii,abjj ,我们要证明 abiiabjjabjiabij 。由于集合按照非递增顺序排列,所以 aiaj,bibj ,又 ai,aj 是正数且 bibj0 ,得 abibjiabibjj ,两边同乘 abjiabjj abiiabjjabjiabij

你可能感兴趣的:(算法导论)