C++动态规划算法之最优批处理问题

问题描述

在一台超级计算机上,编号为1,2,3···的n个作业等待批处理。批处理的任务就是将这n个作业分成若干批,每批包含相邻的若干作业。从时刻0开始,分批加工这些作业。在每批作业开始前,机器需要启动时间S,而完成这批作业所需的时间是单独完成批中各个作业需要时间的总和。单独完成第i个作业所需的时间是 ti,所需的费用是它的完成时刻乘以一个费用系数fi。同一批作业将在同一时刻完成。例如,如果在时刻 T 开始一批作业 x,x+1,x+2···x+k, 则这一批作业的完成时刻均为T+S+(tx+tx+1tx+2···+tk)。最优批处理问题就是要确定总费用最小的批处理方案。

例如,假定有5个作业等待批处理,且S=1,(t1,t2,t3,t4,t5)=(1,3,4,2,1),(f1,f2,f3,f4,f5)=(3,2,3,3,4)如果采用批处理方案(1,2),(3),(4,5),则各作业的完成时间分别为(5,5,10,14,14),各作业的费用分别为(15,10,30,42,56),因此这个批处理方案总费用是153
算法设计:
对于给定的待批处理的个作业,计算其总费用最小的批处理方案。
样例输入
5

1

1 3

3 2

4 3

2 3

1 4
样例输出
153

题目分析

这是一道典型的可以利用动态规划算法解决的问题。我们的子问题是每一个完成某一项工作时的最小费用,完成这项工作的方式可以是和其他工作汇总成批一起完成,也可以是单独完成。我们的方法是从后向前不断寻找最小值,什么意思呢?我们先从后面的工作开始做,用这些已经做过的往前找就可以避免一个回溯的问题。
设z;是批处理作业序列i,i+1,n所需的最小费用。设z;(j)是在批处理作业序列i、
i+1、… n中选取i i+1、… j-1为第1批作业前提下所需的最小费用,即:
z;(j)=zj+(S+ti +t(i+1)+ …t(j-1))(w(i)+w(i+1)+…+w(n))

在这里插入图片描述在我们从后向前遍历的过程中,每次找当前工作的最小费用时,我们都会将其后面已经完成的工作进行打包(由于我们从后向前遍历,所以后面的工作是已经完成的工作)在这些工作中,我们可以将与当前相邻的工作的费用单独赋值给当前最小费用,相当于对上一个工作进行了单独打包。也可以将多个工作打包在一起,这时候我就要遍历这些工作的不同打包方式(单独成批,两个打包成批…,以此类推),找出最小费用,即最优的打包方法,可以花费最少的费用。

总结

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10000;
const int M = 1e4;
int n,s;
int t[N+10],f[N+10];
int tt[N+10],ff[N+10];
int dp[N+10];
int main(){
    cin >> n;
    cin >> s;
    for (int i = 1;i <= n;i++) cin >> t[i] >> f[i];
    for (int i = n;i >= 1;i--){
        tt[i] = tt[i+1] + t[i];
        ff[i] = ff[i+1] + f[i];
    }
    dp[n+1] = 0;
    for (int i = n;i >= 1;i--){
        dp[i] = -1;
        for (int j = i+1;j <= n+1;j++){
            int temp = dp[j] + ff[i]*(s+tt[i]-tt[j]);
            if (dp[i]==-1) dp[i] = temp;
            else dp[i] = min(dp[i],temp);
        }
    }
    printf("%d\n",dp[1]);
    return 0;
}
  • 3
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
旅行商问题(TSP)是一个著名的组合优化问题,它要求找到一条路径,使得从一个起点出发,经过所有给定的点恰好一次,并最终回到起点,使得路径的总长度最小。 动态规划算法是解决TSP问题的一种有效方法,以下是用c++实现动态规划算法解决TSP问题的基本步骤: 1. 定义状态:定义dp[i][j]表示从起点出发,经过集合S中的所有点,最终到达点j的最短路径长度(其中集合S表示除起点和终点以外的所有点的集合)。 2. 初始化状态:对于所有的i和j,初始化dp[i][j]为无穷大。 3. 状态转移方程:对于每个i和j,枚举集合S中的所有点k,更新dp[i][j] = min(dp[i][j], dp[i-{k}][k] + dis[k][j]),其中dis[k][j]表示点k到点j的距离。 4. 最终结果:最终的结果为dp[起点][终点]。 以下是c++代码实现: ```c++ const int N = 20; const int INF = 1e9; int n; // n为点的个数 int dis[N][N]; // dis[i][j]表示点i到点j的距离 int dp[1 << N][N]; // dp[i][j]表示从起点出发,经过集合S中的所有点,最终到达点j的最短路径长度 int tsp() { memset(dp, INF, sizeof(dp)); dp[1][0] = 0; for(int s = 1; s < (1 << n); s++) { for(int i = 0; i < n; i++) { if(!(s & (1 << i))) continue; for(int j = 0; j < n; j++) { if(s & (1 << j)) continue; dp[s | (1 << j)][j] = min(dp[s | (1 << j)][j], dp[s][i] + dis[i][j]); } } } return dp[(1 << n) - 1][0]; // 返回从起点出发,经过所有点,最终回到起点的最短路径长度 } int main() { cin >> n; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { cin >> dis[i][j]; } } cout << tsp() << endl; return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值