资源分配问题:某厂根据计划安排,拟将n台相同的设备分配给m个车间,各车间获得这种设备后,可以为国家提供盈利Ci j(i台设备提供给j号车间将得到的利润,1≤i≤n,1≤j≤m)。问如何分配,才使国家得到最大的盈利?
一、算法思想
1、 动态规划的最优性
算法的最优策略体现在每个子策略都是最优的。由此可以将m个车间划分为前m-1个和第m个,每次分配第m个的时候都是建立在前m-1的最优策略的基础上,再进行最优分配。这样在迭代的过程中就能保证每一次都是最优的,从而到最后的整体最优。
2、 资源分配递归式
确定最大利润时必须先判断前m-1个车间分配的情况,若前m-1分配了k个设备,则第m个只能分配j-k个设备。而如何分配才能时此次分配获得的利润最大,则要比较不同的分配方案所获得的利润:让k从0个设备增长到j,计算前m-1个分配k的价值+第m个分配j-k的价值,取生产价值最大的方案。并记录分配方案即k的值。
如共有3个车间2个设备,就有三种分配方案,分别是:前2个车间分配2个,第三个分配0个;前2个车间分配1个,第三个分配1个和前2个车间分配0个,第三个分配2个。在这些利润中挑选价值最高的方案,所以递归式如下:
Value[i][j]=max(Value[i][j],Value[i-1][k]+profit[i][j-k])
其中1≤i≤m 1≤j≤n 0≤k≤j (m为车间数,n为设备数) profi数组保存的是i号车间分配j台设备所能产生的利润。Value[i][j]的初始化均为0。
而这个也有限制条件,就是当车间数为0或是设备数为0的时候产生利润为0.即:
Value[i][j]=0 (i=0 或 j=0)
3、建表&填表
由于若使用递归方法则需要不断地去调用函数,这样比较浪费时间,所以用动态规划二维数组的方式来实现。
随机生成的数据中,需注意价值表和最大利润表当i=0和j=0时,最大利润和价值都为0;同一个车间随着设备数的增加,能生产的价值也相应递增,所以在初始化的时候要注意这两点。
可以看出每个车间分配不同台设备的数量产生的价值需要放在一个二维数组中,二维数组中的值表示第i号车间分配j台设备时产生的利润;而我们计算i个车间j台设备产生的最大利润又需要放在另一个二维数组中;最后产生最大利润的分配方案也要放在一个二维数组中,表示前i个车间分配j台机器时分配的设备数量。
利用递归式进行填表即可,最后将生产利润最大的分配方案(j-k)填入路径数组中。
伪代码如下:
For i 1 to m//m为车间数
Forj 1 to n //n为设备数
Max=0;
Fork 0 to j
Ifmax< Value[i-1][k]+profit[i][j-k] //递归式
Max= Value[i-1][k]+profit[i][j-k];
Value[i][j]=max;//记下最大利润
Road[i][j]=j-k;//记下路径
由此可以看出一共需要三个二维数组表,这个占用内存很大,所以需要算法进行优化。
4、对随机生成利润表进行优化:
将其变成一维数组的方式就是边生成边利用。从递归式可以看出,每一次计算最大利润只要利用到利润表中同一行的利润数据,所以我们可以生成一行的利润后直接利用。再次生成时覆盖原有的数据就可。这样就将一个利润二维数组优化为一位数组。
伪代码如下:
Profit[0]=0
For I i to m
profit[i]=profit[i-1]+rand()+1;//保证每一行递增
//然后把profit数组传入函数中计算。
5、对最大利润生成表进行优化:
从递归式也看得出,每一次产生最大利润所需要的数据只有上一行的利润。所以我们可以只保存最大利润在一维数组中,然后每次从后往前覆盖。因为计算第i行第j个的时候需要比较第i-1行的前j个,所以只能从后往前覆盖,以免造成数据丢失。
伪代码如下:
For i 1 to m
For jn to 1
Max=0
Fork j to 0
Ifmax<value[k]+profit[j-k]
Max= value[k]+profit[j-k]
Value[j]=max
//然后再将分配数不等于0的车间和设备信息传入结构体内
P[x].shebei=j;
P[x].chejian=i;
P[x].num=j-k;
6、对路径记录数组进行优化:
将路径数组改成用结构体表示。结构体内含三个int型数据,分别表示设备总数(j),车间数(i)和分配的设备数(j-k)。通过结构体数组记录当分配设备数不为0的时候的相关数据,减少空间。但是这个取决于分配数非0时的大小,也就是路径数组的稀疏程度。若不够稀疏,则有可能更耗时间,空间优化也未必达到想要的效果。
结构体定义如下:
Struct path{
Intshebei;
Intchejian;
Int num;
}P[10000];
7、蛮力法:
将每个车间的分配情况用一个序列表示,假设有4个车间,5个设备,则用序列(0,0,3,2)表示设备的分配情况。如第三个数字为3表示第三个车间分配设配3个。
计算所有可能的方法则是利用排列组合的方法计算。假设有3个车间,2个设备。则每个车间有3种可能——0,1,2 一共有3个车间,所以共有33种可能。
将每种可能转换成相应的序列,如20可被3取余,得到序列(0,2,0,2),但序列总额大于设备数,故此情况需要被舍弃。如6可被3取余,得到序列(0,0,2,0),则可计算下一步——计算此分配方案的利润。
伪代码如下:
Num=(n+1)m //一共有这么多种可能
Max=0
For I 0 to num
Sum=0
If(Conversion(I,c))//conversion函数用于转换成序列,返回true or faluse
For j 0 to m
Sum=sum+value [j+1][c[j]]//统计利润
If sum >max
Max=sum;
Flag=i//记录最大利润的情况
Convers(i,n)//主要是将i转换成(n+1)进制的序列
while(i!=0&& m>=0)
{
m=m-1;
inty=i%(n+1);
c[m]=y;//用数组记录取余结果,表示第m个车间分配y台设备
i=i/(n+1);
}
Forj 0 to n
sum=sum+c[j];
if(sum==n)//最后判断序列代表的分配的设备数之和是否等于我们的总设备数
returntrue;
#include <iostream>
#include <cmath>
#include <ctime>
using namespace std;
void primary(int n,int m);
void violent(int n ,int m);
bool conversion(int i,int c[],int n,int m);
void roombetter(int n,int i);
int value[12601][12601];
int distribute[12601][12601];//记录最大利润
int road[12601][12601];
int result[51001];
int makevalue[51001];
int total=1;struct path { int shebei; int chejian; int num; }p[50001]; int main() { cout<<"请输入选择:1、未优化前 2、蛮力法验证 3、空间优化后: "; int t; cin>>t; int n,m; cout<<"请输入车间数m和设备数n: "; cin>>m>>n; if(t==1 || t==2) { int i,j,k; for(i=0;i<=m;i++) for(j=0;j<=n;j++) { distribute[i][j]=0; road[i][j]=0; } for(i=0;i<=n;i++) value[0][i]=0; for(i=1;i<=m;i++) { value[i][0]=0; for(j=1;j<=n;j++) { value[i][j]=value[i][j-1]+rand()%100+1;//保证同一个车间,设备数增加的时候利润增加 // cout<<value[i][j]<<" "; } // cout<<endl; } if(t==1) primary(n,m);//动态规划算法 if(t==2) violent(n,m);//蛮力法 } if(t==3)//空间优化算法 { int i,j,k; for(i=0;i<=n;i++) result[i]=0; clock_t start, end; start = clock(); for(i=1;i<=m;i++) { makevalue[0]=0; for(j=1;j<=n;j++) { makevalue[j]=makevalue[j-1]+rand()%100+1; // cout<<makevalue[j]<<" "; } // cout<<endl; roombetter(n,i); } end = clock(); k=n; for(i=m;i>=1;i--) { for(j=total-1;j>=1;j--) if(p[j].chejian==i && p[j].shebei==k) { cout<<"第"<<i<<"号车间分配了"<<p[j].num<<"台设备。"<<endl; k=k-p[j].num; break; } if(j==0) { cout<<"第"<<i<<"号车间分配了0台设备。"<<endl; } } cout<<"最大价值为:"<<result[n]<<endl; // cout<<total-2<<endl; cout<<"运行时间为:"<<(double)(end - start) / (CLOCKS_PER_SEC)<<"s"<<endl; } return 0; } void primary(int n,int m) { clock_t start, end; start = clock(); int i,j,k; for(i=1;i<=m;i++) for(j=1;j<=n;j++) for(k=0;k<=j;k++) { if(distribute[i][j]<distribute[i-1][k]+value[i][j-k])//递归式,选出生产利润最大的分配方法 { distribute[i][j]=distribute[i-1][k]+value[i][j-k]; road[i][j]=j-k; } } end = clock(); for(i=0;i<=n;i++) cout<<i<<" "; cout<<endl; for(i=1;i<=m;i++) { cout<<i<<" "; for(j=1;j<=n;j++) cout<<road[i][j]<<" "; cout<<endl; } k=n; for(i=m;i>=1;i--){ cout<<"第"<<i<<"号车间分配了"<<road[i][k]<<"台设备。"<<endl; k=k-road[i][k]; } cout<<"最大利润是:"<<distribute[m][n]<<endl; cout<<"运行时间为:"<<(double)(end - start) / (CLOCKS_PER_SEC)<<"s"<<endl; } void violent(int n,int m) { int i,j,profit,max=0,flag; int num=pow(n+1,m); int c[10]; for(i=0;i<num;i++) { profit=0; if(conversion(i,c,n,m))//convers转化为序列 { for(j=0;j<m;j++) profit=profit+value[j+1][c[j]];//计算此种情况分配的利润 if(profit>max) { max=profit;//记录最大利润的分配方案 flag=i; } /* for(j=0;j<m;j++) cout<<c[j]<<" "; cout<<profit<<endl;*/ } } conversion(flag,c,n,m); for(i=0;i<m;i++) cout<<"第"<<i+1<<"号车间分配了"<<c[i]<<"台设备。"<<endl; cout<<"最大利润是:"<<max<<endl; } bool conversion(int i,int c[],int n,int m) { int j,sum=0; for(j=0;j<m;j++) c[j]=0; int number=m;//m为车间数 while(i!=0 && m>=0) { m=m-1; int y=i%(n+1);//y的范围0~n c[m]=y; i=i/(n+1); } for(j=0;j<number;j++) sum=sum+c[j]; if(sum==n) return true; else return false; }void roombetter(int n,int i) { int j,k,max; for(j=n;j>=1;j-- ) { max=0; for(k=j;k>=0;k--) { if(max<result[k]+makevalue[j-k]) { max=result[k]+makevalue[j-k]; result[j]=max; if((j-k)!=0)//当分配设备数不为0的时候记录结构体 { int x=p[total-1].chejian; int y=p[total-1].shebei; if(x!=i || y!=j) { p[total].chejian=i; p[total].shebei=j; p[total].num=j-k; // cout<<total<<" "<<p[total].chejian<<" "<<p[total].shebei<<" "<<p[total].num<<endl; total++; } else if(x==i && y==j) p[total-1].num=j-k; } } } } }