有意思的题目

来源:互联网 发布:四九算法偶数是男 编辑:程序博客网 时间:2024/05/16 18:36
首先我承认我很无聊,不然不会干这个事
今天被人问了个问题,觉的很有意思,就做了一下。发现确实有意思。

题目是:给你n个数,然后找出k个数,是的这k个数的和最接近m

细想了一下这就是个背包问题的变种。
但是很普通的背包问题又不同,因为一般背包问题一般就一个解,而且对物品的数量没限制(选多少随便你)。这个是定死了是K个,所以就是要求装背包,然后还要求物品数量是K。

我想的解答过程是,先就是普通的背包解法,因为普通的背包会有很多个解答,但是肯定又一个解答是K(这个不一定有K个的,之后会详说),这个K的解答就是这个问题的答案。

所以第一步就是做一个可以解出所有解答的背包问题的解答。

这用动态规划法+减枝 借这个背包问题(不明白什么意思的自己去看书)

程序分成三个文件
Knapsack.m

function result = Knapsack(Back_weight,Article)

global article_mark;
global article_result;

%Author:Q
�te:不详
�scribe:背包问题解法
%不同于普通的背包,这个函数解出的是符合背包的所有的解

%参数说明:
  输入参数:
      Back_weight     背包容量
      Article         物品权重矩阵
%
  输出参数:
      result          所有符合这个背包的解
        

[Lengthr,Lengthc] = size(Article);

article_mark = zeros(1,Lengthc);
article_result = [];

article_all_number = Lengthc    ;
back_all_weight    = Back_weight;
article            = Article    ;

article_number     = 1;
back_weight        = 0;

BackTrack(article_all_number,back_all_weight,article,article_number,back_weight);

result = article_result;


BackTrack.m

function result = BackTrack(article_all_number,back_all_weight,article,article_number,back_weight)

global article_mark  ;
global article_result;

%Author:Q
�te:不详
�scribe:单个背包的递归函数

%参数说明:
  输入参数:
      article_all_number      物品总数
      back_all_weight         背包还能装下的权重
      article                 物品权重的矩阵
      article_number          当前背包中物品的数量
      back_weight             背包容量
%
  输出参数:
      result                  最外面的函数result article_result;返回所有的递归结果
%
  全局参数:
      article_mark            存放正在进行的标签
      article_result          存放符合的结果

if article_number article_all_number
    judge_article_mark = article_mark;
    judge_article_mark = judge_article_mark*article';
    
    if sum(judge_article_mark) == back_all_weight
        article_result = [article_result ; article_mark];
    end
else
                                          %没有等于的,但是有不大,不小的,所以剩下的就是只能是等号了
    if back_weight + article(article_number)  back_all_weight
    else
        article_mark(article_number) = 1;
        BackTrack(article_all_number,back_all_weight,article,article_number + 1,back_weight + article(article_number));
    end
    article_mark(article_number) = 0;
    
    if bound_weight(article_number + 1,article) back_all_weight - back_weight
    else
        BackTrack(article_all_number,back_all_weight,article,article_number + 1,back_weight);
    end
end
 

bound_weight.m

function result = bound_weight(number,article)

%Author:Q
�te:不详
�scribe:就是个背包的家和函数
%当时好像是不想用matlab自带的,所以自己写了一个

%参数说明:
  输入参数:
      number      标记,记录是不是被装入背包
      article     每个物品的权重
%
  输出参数:
      result      家和的结果

[Lengthr,Lengthc] = size(article);
sum = 0;

for = number:Lengthc
    sum =sum +  article(m);
end

result = sum;


这个程序的作用是找出背包问题所有的解法

例如:
背包容量  weight =15;
物品质量  article = [9 6 4 2];

这个有两解答  
9 + 6 = 15
9 + 4 + 2 = 15

测试例子:

clear all
clc

weight = 15;
article = [9 6 4 2];

resutl = Knapsack(weight,article);
resutl
 

 运行结果:
有意思的题目

每一行是一个结果,为1表示选中

回到刚刚的问题,如果K = 3,那么就是找3个数,那么第一行就是不符合结果的。
那么第二行就是解答。

所以有一个循环可以找1的数目,那么这个问题就算解决了。

但是这个问题还有一个条件,他是找最接近M的,并不是等于M的,所以可能得到的结果是个空集

解决这个问题的就是以当前的M为中心,左右找点,去用刚刚的这个方法去试,如果有解答,那么这个解答就是最接近的解答。。

所以最后来一左右点寻找的循环就可以了


clear all
clc

= 3;                      %找出k个数
article = [9 6 4 2];        %n个数
weight = 16;                %和为m

judge = 0;

upvalue = 0;
downvalue = 0;
result = [];

while judge == 0
    tmpweight1   = weight + upvalue;
    resutl1      = Knapsack(tmpweight1,article);
    
    [row1 column1] = size(resutl1);
    
    if row1 == 0
    else
        tmpjudge1 = 0;
        
        
        for = 1:row1
            if (sum(resutl1(i,:)) == k)     %发现符合条件的
                tmpjudge1 = 1;
                result = resutl1(i,:);
            end
        end
        
        if tmpjudge1 == 1
            judge = 1;
        end
    end
    
    tmpweight2   = weight + downvalue;
    resutl2      = Knapsack(tmpweight2,article);
    
    [row2 column2] = size(resutl2);
    if row2 == 0
    else
        tmpjudge2 = 0;
        
        
        for = 1:row2
            if (sum(resutl2(i,:)) == k)     %发现符合条件的
                tmpjudge2 = 1;
                result = resutl2(i,:);
            end
        end
        
        if tmpjudge2 == 1
            judge = 1;
        end
    end
    
    upvalue = upvalue + 1;
    downvalue = downvalue - 1;
end

disp('最接近的为');

[frow fcolumn] = size(result);

tmpsum = 0;

for = 1:fcolumn
    if (result(i) == 1)
        article(i)
        tmpsum = tmpsum + article(i);
    end
end

disp('和为');
tmpsum
有意思的题目

  运行结果:

问题解决了,
当前的M的值是16,这个背包是没有解答的,但找出了一个15的解答,15和16差1,所以最接近16的K =3个数据为
9 + 4 + 6 = 15
原创粉丝点击