# 游戏常用算法:动态规划算法-01背包问题
动态规划的原理
动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果。但不同的是,分治法在子问题和子子问题等上被重复计算了很多次,而动态规划则具有记忆性,通过填写表把所有已经解决的子问题答案纪录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。
最优性原理是动态规划的基础,最优性原理是指“多阶段决策过程的最优决策序列具有这样的性质:不论初始状态和初始决策如何,对于前面决策所造成的某一状态而言,其后各阶段的决策序列必须构成最优策略”。
题目描述
有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
只要你能通过找规律手工填写出上面这张表就算理解了01背包的动态规划算法。
首先要明确这张表是从上到下,从左到右生成的。
01背包的转换方程
f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ), f[i-1,j] }
i表示第几行,也就是有几个物品,如上图灰色列;j表示当前背包格子的承重,如上图蓝色标题。
Wi指第j件物品的重量,Pi指第i件物品的价值
c#代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bag01
{
class Program
{
public class BagItem
{
public string Name = "";
public int Value = 0;
public BagItem()
{
Name = "";
Value = 0;
}
public BagItem(string name, int value)
{
Name = name;
Value = value;
}
};
//--------------------------------------------背包问题------------------------------------------
const int maxBagWeight = 10;
static string[] Name = { "a", "b", "c", "d", "e" };
static int[] Weight = { 2, 2, 6, 5, 4 };
static int[] Value = { 6, 3, 5, 4, 6 };
static int nCount = Weight.Length;//物品的总个数
static void Calc01Package(BagItem[,] itemBuffer)
{
//首先进行参数检测
if (null == itemBuffer)
return;
//最后一行,以为只有最后一个物品(第5列)Weight[5] = 4,所以格子小于4的都是0,放不进去,大于等于4的,放入价值是Value[5]=6
for (int curBagWeight = 0; curBagWeight <= maxBagWeight; ++curBagWeight)
{
//如果当前承重量小于最后一个物品的重量,放不进去,价值为0
if (curBagWeight< Weight[0])
{
itemBuffer[0,curBagWeight].Value = 0;
itemBuffer[0,curBagWeight].Name = "";
}
//如果当前承重量大于最后一个物品的重量,放人最后一个物品的价值
else
{
itemBuffer[0,curBagWeight].Value = Value[0];
itemBuffer[0,curBagWeight].Name = Name[0];
}
//cout << memory[maxItemCount][curBagWeight] << " ";
}
//接下来就是动态规划的体现,对剩下的n-1个物品放入,也就是填充memory数组
for (int row = 1; row < Name.Length; ++row)
{
for (int curBagWeight = 1; curBagWeight <= maxBagWeight; ++curBagWeight) //格子编号,相当于格子的承重数
{
if (curBagWeight < Weight[row]) //这里保持和下面一样就可以了
{
itemBuffer[row,curBagWeight].Value = itemBuffer[row - 1,curBagWeight].Value;
itemBuffer[row,curBagWeight].Name = itemBuffer[row - 1,curBagWeight].Name;
}
else
{
itemBuffer[row,curBagWeight].Value = itemBuffer[row - 1,curBagWeight].Value > itemBuffer[row - 1,curBagWeight - Weight[row]].Value + Value[row] ?
itemBuffer[row - 1,curBagWeight].Value : itemBuffer[row - 1,curBagWeight - Weight[row]].Value + Value[row];
itemBuffer[row,curBagWeight].Name = itemBuffer[row - 1,curBagWeight].Value > itemBuffer[row - 1,curBagWeight - Weight[row]].Value + Value[row] ?
itemBuffer[row - 1,curBagWeight].Name : itemBuffer[row - 1,curBagWeight - Weight[row]].Name + Name[row];
}
}
}
}
static void Main(string[] args)
{
BagItem[,] itemBuffer =
{
{ new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem() },
{ new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem() },
{ new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem() },
{ new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem() },
{ new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem(), new BagItem() },
};
Calc01Package(itemBuffer);
Console.WriteLine("背包价值");
for (int row = 0; row < nCount; ++row)
{
for (int col = 0; col <= maxBagWeight; ++col)
{
Console.Write("{0,-5}", itemBuffer[row,col].Value); //使用printf在这里比较方便指定行宽
}
Console.WriteLine();
}
Console.WriteLine("物品放置详情");
for (int row = 0; row < nCount; ++row)
{
for (int col = 0; col <= maxBagWeight; ++col)
{
if(string.IsNullOrEmpty(itemBuffer[row, col].Name))
{
Console.Write("{0,-5}", ".");
}
else
{
Console.Write("{0,-5}", itemBuffer[row, col].Name);
}
}
Console.WriteLine();
}
Console.ReadKey();
return;
}
}
}
运行结果