题目介绍
题目摘选自《计算机算法设计与分析(第5版)》,作者是王晓东,题目如下
分析
这道题也可以运用动态规划的算法解决,乍一看好像看不出来有什么最优子结构的性质,这道题目我们不能把它看作是最长升序子序列那样,一个一个元素拿出来分解成若干个含有重复的子问题,这种拆分的办法这个问题不适用。
这道题我们需要以分组为单位进行拆分,比如说题目给的例子{1,2}{3}{4,5},这是原问题的最优解,我们把第一组拆分出来,那么剩下的{3}{4,5}这两组实际上就是子问题1:3,4,5这三个作业中的最优解,而如果我们只是单纯的把1拿出来,那么剩下的{2}{3}{4,5}可不一定就是子问题2:2,3,4,5中的最优解了。我们弄清楚这个问题之后,就可以仔细分析,写出状态转移方程了。
假如说我们以作业2为例,前面的子问题1的最优解为{3}{4,5},假设这个子结构的花销为dp[2](数组从0开始),那么我们有很多种选择方法,比如{2}{3}{4,5},那么这个对应的值就是dp[2],也就是2本身一组的自己开销,加上{3}{4,5}的花销(注意这里的花销是{3}{4,5}在第二个作业处理的时间内造成的花销,因为题目要求,第二个作业处理结束之后的时间内的开销就是dp[2])。
再举一个例子,比如我们选择{2,3}{4,5}的分组方式,也就是我们选择的切分点在第三个元素,那么就是dp[3](注意这里是第四个元素的dp值),也就对应了{4,5}在{2,3}组处理之后的时间段内造成的开销,我们还要加上数组{2,3}和数组{4,5}在处理{2,3}这段时间内造成的开销。
对于所有的切分方式,我们都要进行计算,要找到一个造成最终的开销最小的点,这个点就是子问题的答案。
所以状态转移方程为:
dp[i] = min(dp[i],dp[j]+(S+ti+ti+1+…+tj-1)*(fi+fi=1+…+fn-1))
后面的开销之所以要把从i往后的开销都要算上的原因在上面已经具体说明。
代码
//program:Optimal batch problem
//author:William.L
//version:v 1.0
#include <iostream>
#include <fstream>
#define MAXN 1000000000
using namespace std;
int my_min(int a,int b){
return (a < b) ? a : b;
}
int my_accumulate(int* src,int str,int ends){
if (str > ends){
return -1;
}
int sum = 0;
for (int i = str;i <= ends; i++){
sum += src[i];
}
return sum;
}
int main(){
int num;//works number.
ifstream in("test file.txt");
int S;//the machine's start time.
in >> num >> S;
int t[num+1],f[num+1];//t means time , f means cost per time.
for (int i = 0;i < num; i++){
in >> t[i] >> f[i];
}
t[num] = f[num] = 0;//add one after the last work to calculate.
in.close();
int dp[num+1];//the array dp[i] means the lowest cost of the array start with the i'th work.
for (int i = 0;i < num; i++){
dp[i] = MAXN;
}
dp[num] = 0;//the the after the last is 0
for (int i = num-1; i >= 0; i--){
for(int j = i + 1; j <= num; j++){
dp[i] = my_min(dp[i],dp[j]+ ((S + my_accumulate(t,i,j-1)) * my_accumulate(f,i,num-1) ) );
//This is the most important step, find the good position to cut the array(point j)
//the number after j use the best answer of from j to n-1
//the number before j, put the works from i to j-1 together.
}
}
ofstream out("test file.txt",ios::app);
out << dp[0] << endl;
out.close();
return 0;
}