一个问题带来的学习

 【故事】

  从前,有一个地主,他拥有很多座粮仓, 分别存储了不同单位的粮食,分布在该城镇的不同地点,短时间内也难以再操作。有一天,山大王杀到他家里,要挟道:“我知道你有好几座粮仓,现在我正在招兵买马,粮草不足,您老救济一下吧,交出几座粮仓,保您老身体健康,发财大吉啊!要过这个冬天,弟兄们算了下需要30单位的粮食,我明天来取!”
    山大王走了之后,地主召集了所有人想想办法。大家集思广益,纷纷给出建议,你一言我一句。“请官兵来”,“就说你只有两座粮仓好了”,......
    地主是个胆小的人,而且考虑了各种情况还是决定要给出这30单位的粮食。就叫众人看看如何选择粮仓,损失最少。最后的结果就是:把各个粮仓的粮食计算一下,选择刚好满足30单位的粮仓就可以了。众人纷纷使出了自己灵活的大脑,快速的计算起来了。大家想了半天,也还没拿出最终的方案。地主这个时候看到管家这悠闲的思索着,便问道:你有何良策?管家笑道:要满足山大王30单位的粮食,也就是选择几座粮仓的粮食之和刚好满足30,直接去计算不免有点乏力。不过可以从相反的角度来思考:我们的目的是要使得交出去的量最小。那么从相反的角度来说,如果我们去找那些能够让自己留下来的粮食最多的粮仓,那么剩下来的粮仓也就是要交给山大王的。(大赞建凡)
    在最终选择了粮仓之后啊,突然有人给地主送来一匹良驹,据说日行千里,背驮万顷。地主大喜,命令去选择好的粮去把多余30的粮食给偷偷运回来。可是去哪一座粮仓就疑惑了,如果用良驹去跑所有选择的粮仓查看是否可以,时间上根本来不及。地主问管家:选择的粮仓中是不是肯定有一座是大于这个多余的粮食的。管家想了想,说道:我不敢跟你直接说这个结果,但是我可以这么跟您说,假设这些选择的粮仓中所有的粮食都小于多余的,如此如此,结果根本不可能,所以肯定有一个是大于多余的量的,因此查一下各个粮仓的剩余量,也就可以了。

------------------------------------------------

【思考】

  抽象出来两个问题:

1. 有一个序列X = {X1, X2, ..., Xn}。对于X的任意子集X′ = {x| xi∈X},求Min(ΣX′) > Y(Y是一个阈值)。也就是就一个子序列,满足两个条件:

(1)子序列之和要大于Y

(2)满足条件(1)中和最小的子集

2. 对于上面求出来的子序列Xk = {x| xi∈X},设ΣXk = S, R = S - Y。证明肯定存在一个xi(xi∈Xk)满足:xi >=  R。

你也先别看,思考一下有什么好的办法去解决这个问题,高手有什么别的建议或者更好的办法也希望能分享给小弟,多谢!^_^

【思路】

1. 问题1

  整体的思路如下:

(1)想到背包问题,但是有一点不一样。背包问题是刚好小于背包的大小,而这题是大于。但是还是非常符合动态规划的子问题重叠,但是一直没转换成动态规划解题。

(2)分而治之。朋友的思路。

(3)第二天一早,朋友就给我电话,说可以这样:背包问题是求给定大小下的最大价值问题,此题反过来思考就是一个背包问题,把序列和减去阈值,然后求满足这个容量的背包问题,最后没有被选中的就是此题的结果!直接大赞啊!^_^

 2. 问题2

  想了一会,然后想到反证法,也就很快看到结果了。毫无疑虑的往下写代码了。

证明:

设x1+x2+...+xn >= Y,并且x1+x2+...+xn-xi < Y(i=1,n), R = x1+x2+...+xn - Y

假设不存在一个xi 满足xi >= R,即所有的xi(i=1,n), 都有xi > R

因为x1+x2+...+xn-xi < Y

所以x1+x2+...+xn < Y+xi < Y+R

与x1+x2+...+xn = Y+R矛盾

所以证明成立!

 

【结果】

  逻辑没有问题了,问题也就迎仍而解了!还是贴一段代码(参考网上改动了一点内容)

typedef struct _tagLYGoods
{
    int nValue;
    int nWeight;
}LYGoods;

int LYMax(int a, int b)
{
    return ((a > b) ? a : b);
}

int LYPrintPack(int **c, LYGoods *A, int nItem, int nSize, std::vector<int> &vecRet)
{
    int *Ret = new int[nItem];
    for (int i = 0; i < nItem; i++)
        Ret[i] = 0;
    for (int i = nItem; i > 0; i--)
    {
        if (c[i][nSize] > c[i - 1][nSize])
        {
            Ret[i - 1] = 1;
            nSize -= A[i - 1].nWeight;
        }
    }
    cout << "PrintPack: " << endl;
    for (int j = 0; j < nItem; j++)
    {
        if (Ret[j] == 0)
            vecRet.push_back(j);
        cout << Ret[j] << ", " << A[j].nWeight << ";";
    }
    cout << endl;

    delete[] Ret;
    return true;
}

int LYPack(LYGoods *A, int nItem, int nSize, std::vector<int> &vecRet)
{int **MN = new int*[nItem + 1]();//定义一维动态数组
    for (int i = 0; i<nItem + 1; i++)//用循环令一维数组变成二维
    {
        MN[i] = new int[nSize + 1];
    }
    int nMax = 0;
    for (int i = 0; i < nItem; i++)
    for (int j = 0; j < nSize; j++)
        MN[i][j] = 0;
    for (int i = 1; i <= nItem; i++)
    {
        for (int j = 1; j <= nSize; j++)
        {
            if (j < A[i - 1].nWeight)
            {
                MN[i][j] = MN[i - 1][j];
            }
            else
            {
                MN[i][j] = LYMax(MN[i - 1][j], MN[i - 1][j - A[i - 1].nWeight] + A[i - 1].nValue);
            }
        }
    }

    LYPrintPack(MN, A, nItem, nSize, vecRet);

    int nRet = MN[nItem][nSize];

    for (int i = 0; i<nItem + 1; i++)
    {
        delete[] MN[i];
    }
    return nRet;
}

(1)结果:

序列:2, 11, 15, 20, 32, 阈值:31

得到的结果是:11, 20

 

(2)随机结果:

  

【总结】

1. 很高兴碰到一个这样的问题,而且静下心来去解决

2. 交流是很重要的,比一个人学习的收益要大很多^_^

3. 动态规划还真是没有搞懂!继续学习!

 

posted @ 2014-09-03 21:59  上山老人  阅读(189)  评论(0编辑  收藏  举报