一个问题带来的学习

 【故事】

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

时间: 2024-08-06 02:52:00

一个问题带来的学习的相关文章

《一个销售的VMWARE学习系列之一 ----虚拟化的理解》

作为一个销售,面对客户的不同问题需求,为了能更好的解决客户的不同问题,以及将产品的优势展现出来,所以下定决心我也要学习VMWARE虚拟化与云服务. 首先,经常会被客户问起一些关于虚拟化的问题?如下: 1.虚拟机稳定吗? 2.为什么要使用虚拟化啊? 3.虚拟化能带来什么好处啊? 4.主机坏了,是不是所有虚拟机都不能用啊? 5.虚拟机容易管理吗? 6.可以把现在的物理机迁移到虚拟机上吗? 7.虚拟机怎样备份啊? 为了能更好的解决客户的问题,我也问了度娘很多问题,下面就简单的一一解答: 问题一:虚拟机

作为一个新人,怎样学习嵌入式Linux?

作为一个新人,怎样学习嵌入式Linux?被问过太多次,特写这篇文章来回答一下. 在学习嵌入式Linux之前,肯定要有C语言基础.汇编基础有没有无所谓(就那么几条汇编指令,用到了一看就会).C语言要学到什么程度呢?越熟当然越好,不熟的话也要具备基本技能.比如写一个数组排序.输入数字求和什么的.学C语言唯一的方法是多写程序多练习,编译出错没关系,自己去解决:执行出错没关系,自己去分析.以前我是用VC来练习C语言的,经常去尝试着写一些C语言竞赛的题目.它们是纯C.纯数学.纯逻辑的题目,不涉及界面这些东

Linux 我的第一个makefile(Linux指令学习笔记)

我的第一个makefile 最近学到了makefile的文件的编写.makefile是一个能达到方便编译链接生成目标程序的文件, make确实很方便,在写makefile的过程也能更好的理解gcc编译器执行生成可执行文件的全过程.这里是我 的第一个makefile的编写. makefile根据依赖,逐步解析生成目标文件. 首先指定all:ef  生成最终目标文件ef 生成ef的前提是依赖ef:total.o sum.o mul.o 依赖三个目标文件. 所以我们要另外指定gcc的执行 gcc -o

一个Swifter的Kotlin学习——Kotlin 基本语法

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 一个Swifter的Kotlin学习--Kotlin 基本语法 - u012903898的博客 - 博客频道 - CSDN.NET u012903898的博客 目录视图 摘要视图 订阅 [活动]2017 CSDN博客专栏评选 &nbsp [5月书讯]流畅的Python

《一个销售的VMWARE学习系列之三:在ESXI主机上安装WINDOWS虚拟机》

上一篇文章已经学会了怎样安装ESXI主机啦,那下面我们就要学,如何在ESXI主机上安装WINDOWS虚拟机啦:ESXI主机是基于LINUX内核啊!是不是要写命令创建WINDOWS虚拟机呢,哈!这个不用担心,VMWARE提供了vSphere Client 管理工具,而且必须使用才能创建虚拟机啊,你想写命令都不行啊. 1.首先,我们找一台客户机安装vSphere Client 管理工具先,选择"中文",确定安装,如下: 2.安装步骤都是一直"下一步"就可以啦,安装完成后

作为一个新人,如何学习嵌入式Linux?

作为一个新人,如何学习嵌入式Linux?被问过太多次,特写这篇文章来回答一下. 在学习嵌入式Linux之前.肯定要有C语言基础.汇编基础有没有无所谓(就那么几条汇编指令,用到了一看就会).C语言要学到什么程度呢?越熟当然越好,不熟的话也要具备基本技能.比方写一个数组排序.输入数字求和什么的.学C语言唯一的方法是多敲代码多练习,编译出错没关系,自己去解决:运行出错没关系.自己去分析.曾经我是用VC来练习C语言的.常常去尝试着写一些C语言竞赛的题目. 它们是纯C.纯数学.纯逻辑的题目.不涉及界面这些

《一个销售的VMWARE学习系列之四:VMware Converter 迁移物理机到 ESXI虚拟机》

VMware Converter有啥用? VMware Converter 是一款能将物理电脑系统.VMware其他版本虚拟机镜像或第三方虚拟机镜像转化为一个虚拟机映像文件的工具,而且生成的映像可以在该公司的VMware虚拟机软件中使用. 为了大家能更好的理解下面的操作,先把网络环境描述一下,如下: 环境解释:就两台主机啦,没有别的啦,一台物理主机XP,一台ESXI主机,中间不要任何服务器去做转换啦:我用的是最简单.最容易.最明白的网络环境啦,哈哈!! 1.我们首先去看一下ESXI主机里面现在有

《一个销售的VMWARE学习系列之二:ESXI的安装步骤》

ESXI是什么?可以理解为VMWARE的操作系统,比如:微软的Windows操作系统啦: 1.首先把光碟插到光盘上启动电脑,在窗口上选择"DELLESXI-5.1-799733(A00) Installer",如下: 2.按回车确定安装ESXI,如下: 3.按F11接受协议,如下: 4.按"Enter",选择安装的硬盘,如下: 5.选择默认的键盘语言,如下: 6.在下面就输入ROOT的密码,并按"Enter"如下: 7.选择"F11&q

y combinator 做的一个调查_可以学习一下

RoR: 在网络营运平台企业中,RoR站稳使用率第一的位置.其用户包括:ZenPayroll (人力资源).Asile50 (零售平台).BackerKit (众筹平台).Rainforest (QA测试)等. Ruby on Rails是一种结合Ruby语言与Rails平台的一种网页编程语言,Ruby语言以自然.简洁.快速著称,全面支持面向对象程序设计,而Rails则是Ruby广泛应用方式之一,在Rails平台上设计出一套独特的MVC开发架构,采取模型(Model).外观(View).控制器(