算法——动态规划之资源分配问题及其优化

文章探讨了动态规划在资源分配问题中的应用,通过分析递推公式,提出两种优化方法:一是调整存储方式以按行遍历,二是利用一维数组进一步降低空间复杂度。同时讨论了在优化后如何存储和获取最优分配方案的挑战。
部署运行你感兴趣的模型镜像

上一篇文章写了动态规划求解0-1背包问题,这里做一道资源分配问题强化理解,顺便分析一下动态规划算法的优化问题。

问题描述:某厂根据计划安排,拟将n台相同的设备分配给m个车间,各车间获得这种设备后,可以为国家提供盈利Ci j(i台设备提供给j号车间将得到的利润,1≤i≤n,1≤j≤m) 。问如何分配,才使国家得到最大的盈利?

分析递推公式
前 i 台设备分配给前 j 个车间,考虑当前第 j 个车间的情况,假设第 j 个车间分配了 k(0 <= k <= i) 个设备,剩下的 i - k 台设备要分给前 j - 1 个车间,此时前 j 个车间利润为p[i][j] = p[i - k][j - 1] + c[k][j],遍历k的情况可取到最大利润,故有下面公式
在这里插入图片描述其中0<=k<=i,1<=i<=n,1<=j<=m

代码如下

int i, j, k;
	for (i = 1; i <= n; i++)
		for (j = 1; j <= m; j++)
			for (k = 0; k <= i; k++)//i代表了设备数目
				if ((p[k][j - 1] + c[i - k][j]) > p[i][j])
				{
					p[i][j] = p[k][j - 1] + c[i - k][j];
					f[i][j] = i - k;//用于当前计算的车间的设备数
				}

JS代码

const c = [[0,0,0],[0,189, 152],[0,200, 333]]
function dynamic (c) {
  let n = c.length - 1
  let m = c[0].length - 1
  let p = (new Array(n + 1).fill(null)).map(() => new Array(m + 1).fill(0))
  for (let i = 1; i <= n; i++) {
    for (let j = 1; j <= m; j++) {
      for (let k = 1; k <= i; k++) { // i代表了设备数目
        // 给j号车间提供多少个设备可以使得利润最大化
        if (p[i - k][j - 1] + c[k][j] > p[i][j]) {
          p[i][j] = p[i - k][j - 1] + c[k][j]
        }
      }
    }
  }
  return p[n][m]
}
console.log(dynamic(c))

优化一
如果你在一个实例中尝试一下自己画表,你会发现,这道公式是按列计算数值的。
举个例子,2台设备分配给2个车间的盈利表(c[i][j])如图所示
这里写图片描述
初始化最大盈利表
这里写图片描述
根据公式填表(p[i][j]=max{p[i][j],p[k][j-1]+c[i-k][j]} )
p[1][1]=max{p[1][1],p[k][0]+c[1-k][1]},0<=k<=1
p[1][1]=max{p[1][1],p[0][0]+c[1][1],p[1][0]+c[0][1]}=max{0,189,0}=189
用于计算的有下面4个数字
这里写图片描述
这样看或许还不够明确,我们来算一下p[2][2]
p[2][2]=max{p[2][2],p[k][1]+c[2-k][2]},0<=k<=2
p[2][2]=max{p[2][2],p[0][1]+c[2][2],p[1][1]+c[1][2],p[2][1]+c[0][2]}
用于计算的有下面几个数字
这里写图片描述
问题浮出水面==>这条递推公式是按照列遍历的!!学过计算机组成原理的朋友可能已经知道了,没错,就是cache的问题。
改变一下存储方式即可让遍历按照行进行!

  • p[i][j]=max{p[i][j],p[i-1][k]+c[i][j-k]} 其中0<=k<=j,1<=i<=m,1<=j<=n
    解释一下这条新公式:其实思想没改变,就是将i、j互换,这里i代表车间数,j代表设备数,公式表示前i个车间瓜分j台设备,考虑当前第i个车间的情况,当前车间可能分配0个设备、1个设备、2个设备、…、n个设备,这其中得到最大利润的方案即为最优方案。
    注意c[i][j]表示与题干不同:它表示j台设备提供给i号车间将得到的利润,1≤i≤m,1≤j≤n
function reverse (arr) { // 数组行列互换
	return arr[0].map((col, i) =>  arr.map(row => row[i]))
}
// 优化:按行读取数组
function dynamic (c) {
  let n = c.length - 1 // 设备总数
  let m = c[0].length - 1 // 车间总数
  let p = (new Array(m + 1).fill(null)).map(() => new Array(n + 1).fill(0))
  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      for (let k = 1; k <= j; k++) { // 设备数量
        if (p[i - 1][j - k] + c[i][k] > p[i][j]) {
          p[i][j] = p[i - 1][j - k] + c[i][k]
        }
      }
    }
  }
  return p[m][n]
}
console.log(dynamic(reverse(c)))

优化二
上面优化后的公式是按照行遍历的,如果你尝试自己手动画图,会发现每次求解只需要用到两行
p[2][2]=max{p[2][2],p[1][k]+c[2][2-k]},0<=k<=2
p[2][2]=max{p[2][2],p[1][0]+c[2][2],p[1][1]+c[2][1],p[1][2]+c[2][0]}
这里写图片描述
c表只用于生成p表,用完就没有作用了,如果我们在生成c表一行的同时生成p表,便只需要一个一维数组c[n+1]存储c表和一个c[2*(n+1)]的二维数组存储p表,空间复杂度下降。
p表能否优化成一维数组呢?
答案是可以的,因为求p表第i行j列数据时只需要用到c表i行和p表i-1行的前j个数据,我们只需要从后往前遍历,便不会影响到其它数据的计算
这里写图片描述
优化后代码如下

void Dynamic_Optimization(int *cc, int *pp)
{
	int i, j, k, u;
	//最开始最大利润表是0
	for(i=0;i<=m;i++)
	{
		pp[i]=0;
	}
	cc[0]=0;
	for (i = 1; i <= m; i++)//m车间数
	{
		for (j = 1; j <= n; j++)
		{
			//初始化一列利润表cc
			cc[j] = cc[j - 1] + rand() % 1000 + 1;
		}
		for (u = n; u >= 1; u--)//从后往前遍历
		{
			int max = 0;
			for (k = u; k >= 0; k--)
			{
				if (pp[k] + cc[u - k] > max)
				{
					max = pp[k] + cc[u - k];
					pp[u] = max;
				}
			}
		}
	}
}

思考:p只是一个一维数组,我们只能通过它得知最大利润,如何得知分配的方案呢?
如果c和p还是二维数组的时候,其实可以不用分配表application,直接算出分配方案,但现在c和p已经优化为一维数组了,没有application我们无法得知分配方案。
那么application表只能是一个二维数组吗?
有的博主是这样做的:通过结构体存储,将application表中数值为0的部分省去。结构体内定义了3个int型数据,这种方法比较适合在0较多的application表中用,否则反而可能增加空间负担,得不偿失!请慎用!

结构体定义如下:

Struct path{

 Int shebei;

 Int chejian;

Int num;

}P[10000];

想到结构体就想到了链表啦,如果像下图这样把每个地方的0压缩,或许可以减少一点空间负担,而且,链表不要求连续的空间
这里写图片描述
如图,把一行变成4个链表块,0的个数保存在链表块里。

您可能感兴趣的与本文相关的镜像

Qwen-Image-Edit-2509

Qwen-Image-Edit-2509

图片编辑
Qwen

Qwen-Image-Edit-2509 是阿里巴巴通义千问团队于2025年9月发布的最新图像编辑AI模型,主要支持多图编辑,包括“人物+人物”、“人物+商品”等组合玩法

实验课程:算法分析与设计 实验名称:用动态规划法求解资源分配问题 (验证型实验) 实验目标: (1)掌握用动态规划方法求解实际问题的基本思路。 (2)进一步理解动态规划方法的实质,巩固设计动态规划算法的基本步骤。 实验任务: (1)设计动态规划算法求解资源分配问题,给出算法的非形式描述。 (2) 在Windows环境下用C 语言实现该算法。计算10个实例,每个实例中n=30, m=10, Ci j为随机产生于范围(0,103)内的整数。记录各实例的数据及执行结果(即最优分配方案、最优分配方案的值)、运行时间。 (3)从理论上分析算法的时间和空间复杂度,并由此解释相应的实验结果。 实验设备及环境: PC;C/C++等编程语言。 实验主要步骤: (1) 根据实验目标,明确实验的具体任务; (2) 分析资源分配问题,获得计算其最优值的递推计算公式; (3) 设计求解问题动态规划算法,并编写程序实现算法; (4) 设计实验数据并运行程序、记录运行的结果; (5) 分析算法的时间和空间复杂度,并由此解释释相应的实验结果; 问题分析: 问题描述: 某厂根据计划安排,拟将n台相同的设备分配给m个车间,各车间获得这种设备后,可以为国家提供盈利Ci j(i台设备提供给j号车间将得到的利润,1≤i≤n,1≤j≤m) 。问如何分配,才使国家得到最大的盈利? 算法基本思想: 本问题是一简单资源分配问题,由于具有明显的最优子结构,故可以使用动态规划求解,用状态量f[i][j]表示用i台设备分配给前j个车间的最大获利,那么显然有f[i][j] = max{ f[k][j–1] + c[i-k][j] },0<=k<=i。再用p[i][j]表示获得最优解时第j号车间使用的设备数为i-p[i][j],于是从结果倒推往回求即可得到分配方案。程序实现时使用顺推,先枚举车间数,再枚举设备数,再枚举状态转移时用到的设备数,简单3重for循环语句即可完成。时间复杂度为O(n^2*m),空间复杂度为O(n*m),倘若此题只需求最大获利而不必求方案,则状态量可以减少一维,空间复杂度优化为O(n)。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值