zoukankan      html  css  js  c++  java
  • 一个问题带来的学习

     【故事】

      从前,有一个地主,他拥有很多座粮仓, 分别存储了不同单位的粮食,分布在该城镇的不同地点,短时间内也难以再操作。有一天,山大王杀到他家里,要挟道:“我知道你有好几座粮仓,现在我正在招兵买马,粮草不足,您老救济一下吧,交出几座粮仓,保您老身体健康,发财大吉啊!要过这个冬天,弟兄们算了下需要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. 动态规划还真是没有搞懂!继续学习!

  • 相关阅读:
    Hbase shell基本操作
    Spring Caching集成Ehcache
    统一认证授权及单点登录的技术选择
    详谈再论JAVA获取本机IP地址
    Spark基础脚本入门实践3:Pair RDD开发
    Spark基础脚本入门实践2:基础开发
    Spark基础脚本入门实践1
    必须熟练的基础linux命令
    Swing中的线程并发处理
    源码分享!!!world文档转换为JPG图片
  • 原文地址:https://www.cnblogs.com/pk-run/p/3883908.html
Copyright © 2011-2022 走看看