这道面试题好难啊!!

rockjohnson 2010-04-15 12:54:30
有一个大水桶,能盛放的水总容积为V,设有N个小桶的水,其容积分别为w1,w2,...,wn,希望从N个小桶中选择若干桶水倒入大桶中,所选水的体积之和刚好能盛满大桶,即水的体积之和等于V。请用非递归的方式编程实现求出所有可能解。
...全文
902 37 打赏 收藏 转发到动态 举报
写回复
用AI写文章
37 条回复
切换为时间正序
请发表友善的回复…
发表回复
chengzhe 2010-04-23
  • 打赏
  • 举报
回复
27楼写的很好呀

把价值都设成1就可以了


27楼对DP理解非常透彻, 建议楼主给他加分 其余都无视
酷python 2010-04-23
  • 打赏
  • 举报
回复
List<int> w=new ArrayList<int>();
将w1,w2,...wn存到w中
生命n个List的对象
先计算所有的小桶水量加在一起,W_all=w1+w2+...+wn

if(W_all<V){
装不满
}else if(W_all==V){
只有一种方法
}else{
for(int i=0;i<n;i++){
list1=new ArrayList<int>(w);
list1.remove(i);
此时,list1中还剩n-1个元素,所有元素相加,和为sum1
if(sum1==V){
打印出这个方法
}else if(sum1<V){
continue;
}else{
for(int i=0;i<n-1;i++){
list2=new ArrayList<int>(list1);
此时list2已经获得了上一层循环后list1中剩余的内容,每次获得的均不相同
list2.remove(i)
此时,list2又去除一个数值,还剩下n-2个数值,求和为sum2
if(sum2==V){
打印此方法
}else if(sum2<V){
continue;
}else{
重复下一个循环,每一次循环都减少一个数字,然后求和,如果等于V就打印这些数值
如果小于V,那么需要继续这一层循环,没有必要进入到下一层循环,因为这一层的所有
数值求和后小于V,下一层把这一层的数值去除一个后在求和,必然也小于V,有多少个桶
就循环多少次,每一层循环都将自己从上一层得到的桶减去一个后求和,根据和来决定是否
向下传递。
}

}
}
}
}
japt88_115656292 2010-04-23
  • 打赏
  • 举报
回复
没高手写个正确答案,然后给点注释吗?有的快啊
LeonTown 2010-04-21
  • 打赏
  • 举报
回复
想了一下,
用DP似乎也可以。

相当于在去掉第wn个小水桶后,
将前n-1个小水桶装入V-wn的大水桶中;
或装入V的大水桶中。

就是相当于分别搜索选择和不选择第wn个小水桶的情况。。。
LeonTown 2010-04-20
  • 打赏
  • 举报
回复
[Quote=引用 28 楼 qq120848369 的回复:]
代码复制了两份,当时没看Primer C++,写的类粗糙,献丑了。。
[/Quote]

但是楼主这个问题,
该如何套用到背包问题上呢?
owt5008137 2010-04-20
  • 打赏
  • 举报
回复
刚发的代码少了一段
看下面这个,上面的代码无视掉吧

for(i = 0; i <= v; i ++)
{
if(a[i] == true)
{
for(j = 0; j < n; j ++)
{
a[i + w[j]] = true;
if(i + w[j] < res && i + w[j] > v)
res = i + w[j];
if(i + w[j] > v)
break;
}
}
if(res == v)
break;
}
owt5008137 2010-04-20
  • 打赏
  • 举报
回复
那上面的谁,有必要写这么长代码么?
先对w从大到小排序,res初始为0x7fffffff
设置a[0] = true,其他为false
然后

for(i = 0; i <= v; i ++)
{
if(a[i] == true)
{
for(j = 0; j < n; j ++)
{
a[i + w[j]] = true;
if(i + w[j] < res)
res = i + w[j];
if(i + w[j] > v)
break;
}
}
if(res == v)
break;
}

代码段运行完后res就是结果
小生我怕怕 2010-04-20
  • 打赏
  • 举报
回复
受教了~
owt5008137 2010-04-20
  • 打赏
  • 举报
回复
背包问题,两个for循环就搞定的
hellodota121 2010-04-19
  • 打赏
  • 举报
回复
类似0-1背包,用回溯法比较容易写出非递归的程序
qzl123666 2010-04-19
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 vshuang 的回复:]
可以动态规划的
[/Quote]
动态规划
LeonTown 2010-04-19
  • 打赏
  • 举报
回复
最简单的就是枚举所有的组合。

可以做一下优化:
去掉wi>V的数据,
对剩下的wi,按从大到小排序。
wn >= ... w3 >= w2 >= w1
如果(w3+w2+w1<V),
在对上述序列,通过二进制位的方式,枚举组合的时候,
可以从0...0111开始向1...1111枚举所有组合。


至于用背包的方法,
还请大虾们指点一下吧。。。
liutailiang2 2010-04-19
  • 打赏
  • 举报
回复
期待高手给出答案。
qq120848369 2010-04-19
  • 打赏
  • 举报
回复
代码复制了两份,当时没看Primer C++,写的类粗糙,献丑了。。
qq120848369 2010-04-19
  • 打赏
  • 举报
回复

#include <iostream>
using namespace std;

//动态规划:0-1背包问题
//bestValue[i][j]=max ( bestValue[i+1][j-w[i]]+v[i] ,bestValue[i+1][j] ) w[i]<=j
//bestValue[i][j]=bestValue[i+1][j] w[i]>j

class Knapsack
{
private:
int *weight;//物品重量数组
int *value;//物品价值数组
int numOfItems;//物品数量
int bagSpace;//背包容量
int **bestValue;//动态规划表格,记录bestValue[i][j]的价值,为最优价值,i表示物品i...n装入容量为j的背包能达到的最大价值
int **path;//为了求出取得最优值时的解,记录动态规划表不同表项的选择与否
public:
//构造函数
Knapsack(int numOfItems,int bagSpace)
{
weight=new int[numOfItems+1];
value=new int[numOfItems+1];
this->bagSpace=bagSpace;
this->numOfItems=numOfItems;

bestValue=new int* [numOfItems+1];
for(int i=0;i<numOfItems+1;i++)
{
bestValue[i]=new int[bagSpace+1];
}

path=new int* [numOfItems+1];
for(int i=0;i<numOfItems+1;i++)
{
path[i]=new int[bagSpace+1];
}
}
//输入物品的重量与价值
void input()
{
int i=1;
while(i<=numOfItems)
{
cout<<"输入第"<<i<<"个物品的重量"<<endl;
cin>>weight[i];
cout<<"输入第"<<i<<"个物品的价值"<<endl;
cin>>value[i];
++i;
}
}
//动态规划核心算法
void knapsack()
{
//初始化递归最底层,即将bestValue[n][0:c]进行初始化
for(int i=0;i<=bagSpace;i++)
{
if(weight[numOfItems]<=i)
{
bestValue[numOfItems][i]=value[numOfItems];
path[numOfItems][i]=1;
}
else
{
bestValue[numOfItems][i]=0;
path[numOfItems][i]=0;
}
}
//递推的进行动态规划,自底向上,最终bestValue[1][bageSpace]为1-n物品放入容量bagSpace内的最大价值
for(int k=numOfItems-1;k>=1;k--)
{
for(int j=0;j<=bagSpace;j++)
{
bestValue[k][j]=bestValue[k+1][j];
path[k][j]=0;//不放入的情况
if(weight[k]<=j)//如果容量足够放入当前物品
{
if(bestValue[k+1][j-weight[k]]+value[k]>bestValue[k][j])//如果放入的价值大于不放的价值
{
bestValue[k][j]=bestValue[k+1][j-weight[k]]+value[k];
path[k][j]=1;//那么就选择放入
}
}
}
}
}
//输出最大价值,并且输出选择方式
void display()
{
//打印出bestValue[1][bagSpace],表示1...numOfItems的物品装入容量为bagSpace的最大价值
int i=1;
int j=bagSpace;
cout<<"最大价值为"<<bestValue[1][j]<<endl;
//根据path[1][bagSpace]的记录开始,递归到path[n][某容量],从而打印出每个物品是否被选择进入背包
while(i<=numOfItems)
{
if(path[i][j]==0)//如果i物品没被放入,看i+1个物品装入容量j背包
{
++i;
}
else
{
cout<<"<重量:"<<weight[i]<<",价值:"<<value[i]<<">"endl;
j-=weight[i];
++i;
}
}
}
};

void main()
{
Knapsack test(5,50);//5个物品,背包容量50
test.input();//输入5个物品的价值与重量
test.knapsack();//动态规划
test.display();//打印选择与最大价值
}
#include <iostream>
using namespace std;

//动态规划:0-1背包问题
//bestValue[i][j]=max ( bestValue[i+1][j-w[i]]+v[i] ,bestValue[i+1][j] ) w[i]<=j
//bestValue[i][j]=bestValue[i+1][j] w[i]>j

class Knapsack
{
private:
int *weight;//物品重量数组
int *value;//物品价值数组
int numOfItems;//物品数量
int bagSpace;//背包容量
int **bestValue;//动态规划表格,记录bestValue[i][j]的价值,为最优价值,i表示物品i...n装入容量为j的背包能达到的最大价值
int **path;//为了求出取得最优值时的解,记录动态规划表不同表项的选择与否
public:
//构造函数
Knapsack(int numOfItems,int bagSpace)
{
weight=new int[numOfItems+1];
value=new int[numOfItems+1];
this->bagSpace=bagSpace;
this->numOfItems=numOfItems;

bestValue=new int* [numOfItems+1];
for(int i=0;i<numOfItems+1;i++)
{
bestValue[i]=new int[bagSpace+1];
}

path=new int* [numOfItems+1];
for(int i=0;i<numOfItems+1;i++)
{
path[i]=new int[bagSpace+1];
}
}
//输入物品的重量与价值
void input()
{
int i=1;
while(i<=numOfItems)
{
cout<<"输入第"<<i<<"个物品的重量"<<endl;
cin>>weight[i];
cout<<"输入第"<<i<<"个物品的价值"<<endl;
cin>>value[i];
++i;
}
}
//动态规划核心算法
void knapsack()
{
//初始化递归最底层,即将bestValue[n][0:c]进行初始化
for(int i=0;i<=bagSpace;i++)
{
if(weight[numOfItems]<=i)
{
bestValue[numOfItems][i]=value[numOfItems];
path[numOfItems][i]=1;
}
else
{
bestValue[numOfItems][i]=0;
path[numOfItems][i]=0;
}
}
//递推的进行动态规划,自底向上,最终bestValue[1][bageSpace]为1-n物品放入容量bagSpace内的最大价值
for(int k=numOfItems-1;k>=1;k--)
{
for(int j=0;j<=bagSpace;j++)
{
bestValue[k][j]=bestValue[k+1][j];
path[k][j]=0;//不放入的情况
if(weight[k]<=j)//如果容量足够放入当前物品
{
if(bestValue[k+1][j-weight[k]]+value[k]>bestValue[k][j])//如果放入的价值大于不放的价值
{
bestValue[k][j]=bestValue[k+1][j-weight[k]]+value[k];
path[k][j]=1;//那么就选择放入
}
}
}
}
}
//输出最大价值,并且输出选择方式
void display()
{
//打印出bestValue[1][bagSpace],表示1...numOfItems的物品装入容量为bagSpace的最大价值
int i=1;
int j=bagSpace;
cout<<"最大价值为"<<bestValue[1][j]<<endl;
//根据path[1][bagSpace]的记录开始,递归到path[n][某容量],从而打印出每个物品是否被选择进入背包
while(i<=numOfItems)
{
if(path[i][j]==0)//如果i物品没被放入,看i+1个物品装入容量j背包
{
++i;
}
else
{
cout<<"<重量:"<<weight[i]<<",价值:"<<value[i]<<">"endl;
j-=weight[i];
++i;
}
}
}
};

void main()
{
Knapsack test(5,50);//5个物品,背包容量50
test.input();//输入5个物品的价值与重量
test.knapsack();//动态规划
test.display();//打印选择与最大价值
}

思路总结: 看到一个题目,首先看问什么,下面以此题举例分析一下。

0-1背包问题

1,问题要求什么?
答:求把n个物品放入容量C的背包内能达到的最大价值

2,转换成一个抽象一点的数学表达式是什么?
答:bestValue[n][C],表示n个物品放入容量C的背包的最大价值

3,不考虑算法应该怎么选择,我们实际去解决这个问题的时候,是从哪里开始去做的?
答:我们有n个物品,C容量背包。 于是我们开始解决问题,我先放第一个物品,如果能放进去,我就放进去,当然,我也可以不放。
第一个物品处理结束以后,我们着手于第二个物品,能放进去就放进去,当然,我们也可以不放。
所以,这就是一个决策问题,决策是从我们实际处理问题中抽象出来的,我们放物品的时候只能一个一个放,决策是放或者不放。

4,在决策了解的情况,我们应该考虑当前要求的bestValue[n][C],在决策放入或者不放入的情况,分别等于什么?
答:如果能够放入,那么我们的背包还有C-w[i], 物品还有n-1个,当然,我们也可以选择不放进去,那么我们背包依旧有C容量,物品还有n-1个。 所以我们修改一下我们对bestValue[n][C]的定义,从而就得到了一个最优子结构的递归公式。

为了我们决策的进行,即我们每次决策都是最第i个物品进行决策,所以bestValue[n][C]修改为best[i][C],表示i,i+1,i+2...n个物品放入容量为C的背包的最大价值。

所以:bestValue[i][j]=max ( bestValue[i+1][j-w[i]]+v[i] ,bestValue[i+1][j] ) w[i]<=j
bestValue[i][j]=bestValue[i+1][j] w[i]>j

意思是:
如果当前容量j装不下物品i,那么i到n装入j的最大价值就等于i+1到n装入j的最大价值,就是公式的第二行。
如果当前容量j可以装下物品i,那么我们可以装进去,当然,也可以犯贱,不装进去,看看结果如何,所以i到n个物品装入j容量背包的最大价值就等于 i+1到n物品装入j-w[i]容量的背包可以达到的最大价值+value[i] ,i+1到n物品装入j容量背包的最大价值,这两种不同决策的一个最大值。

总结:解决什么? 从哪里开始做起? 有哪些决策? 决策后会怎么样?

找出了递归式,它具有最优子结构性质,即可以简单的理解为:当前的最优产生于子问题的最优,然后子问题的最优不受当前最优的影响,并且通过观察递归公式,应该找到递归的最底层的i,j分别是什么,我们观察到i在逐渐增加,j在逐渐减小,所以我们在递推的时候,首先把最底层进行初始化,然后利用递归公式向上递推。 所以我们需要首先初始化bestValue[n][0:C],即记录第n个物品装入0到C的背包的能达到的价值,当w[n]<=j时,bestValue[n][j]等于value[n],如果w[n]>j,即容量不够,那么就是0.

我们能够从底向上递推的重要原因就是:最优子结构+无后效性 。 多多体会吧。 这是基础理解了。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qq120848369/archive/2010/04/01/5441005.aspx



这是我刚学一阵时候写的体会,看看能不能看懂.
qq120848369 2010-04-19
  • 打赏
  • 举报
回复
背包问题的DP,刚学的确很难理解.
LeonTown 2010-04-19
  • 打赏
  • 举报
回复
大家能给个具体的DP的例子吗。。。
oyzdz1988 2010-04-19
  • 打赏
  • 举报
回复
用回溯法来解决0-1背包问题
qq120848369 2010-04-19
  • 打赏
  • 举报
回复
基础知识,入门就好了。
XLL19880206 2010-04-16
  • 打赏
  • 举报
回复
这题的难点在于求出所有可能的解,如何做呢?
加载更多回复(16)

33,010

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧