问题提出:
N个智能体,现在有个任务,就是让N个智能体要到N个目标位置,目标位置不能重复,目标位置与机器人一一对应,要使得期间所有所走的距离之和最短,求解最优任务分配。
问题抽象:
有N个师父, N个徒弟,每个徒弟只能选者一个师父学习, 每个师父只能带一个徒弟; 每个师父有自己的技能,每个徒弟有自己学习的天分, 当徒弟 j 在师父 i 门下学习, 产生效益为 Aij, 问如何安排使得总效益最大?
定义数据格式:
mi:第i个师父,i=1,2,3,⋯,nm_i: 第 i 个师父, i=1,2,3,⋯,nmi:第i个师父,i=1,2,3,⋯,n
tj:第j个徒弟,j=1,2,3,⋯n,t_j: 第 j 个徒弟,j=1,2,3,⋯n, tj:第j个徒弟,j=1,2,3,⋯n,
xij:第j个徒弟是否在第i个师父门下学习:1,是;2不是.x_{ij}: 第j 个徒弟是否在第i 个师父门下学习: 1, 是; 2 不是. xij:第j个徒弟是否在第i个师父门下学习:1,是;2不是.
aij:第j个徒弟在第i个师父门下学习产生的效益.a_{ij}:第j 个徒弟在第i 个师父门下学习产生的效益.aij:第j个徒弟在第i个师父门下学习产生的效益.
y=max∑i=1naij.xij(式0)y=max \sum_{i=1}^n a_{ij}.x_{ij} { ( 式0)} y=maxi=1∑naij.xij(式0)
∑i=1nxij=1,∀j=1,2,3,⋯,n(式1)\sum_{i=1}^n x_{ij} =1 , \forall j=1,2,3,⋯,n { ( 式1)} i=1∑nxij=1,∀j=1,2,3,⋯,n(式1)
∑j=1nxij=1,∀i=1,2,3,⋯,n(式2)\sum_{j=1}^n x_{ij} =1 , \forall i=1,2,3,⋯,n { ( 式2)} j=1∑nxij=1,∀i=1,2,3,⋯,n(式2)
xij∈{0,1}∀i=1,2,3,⋯,n;j=1,2,3,⋯,n(式3)x_{ij} \in {\lbrace0,1}\rbrace \forall i=1,2,3,⋯,n;j=1,2,3,⋯,n (式3)xij∈{0,1}∀i=1,2,3,⋯,n;j=1,2,3,⋯,n(式3)
在这个抽象的例子中,我们把
师父 ⟺\iff⟺智能体(机器人)
徒弟⟺\iff⟺要去的目标位置位置
效益 ⟺\iff⟺智能体(机器人)当前位置与目标位置二者之间距离
当然这个例子中是求最小,师父徒弟的例子是求最大,道理是一样的。
解决分配问题最常用的是匈牙利算法。
按照知乎上某位博主的支付报酬、矩阵求解思路:
若从指派问题的系数矩阵的某行(列)各元素中分别减去或者加上常数k,其最优任务分解问题不变。
这里是引用
通过寻找“0”元素巧妙得出0报酬的指派思路:
给出的代码如下:
#include
typedef struct matrix
{int cost[101][101];int zeroelem[101][101];int costforout[101][101];int matrixsize;
}MATRIX;
MATRIX hungary;
int result[5041][2]; //用来储存解的结果,第一列表示工人第二列表示工件
void zeroout(MATRIX &hungary); //减去行列的最小值得到零元素
void circlezero(MATRIX &hungary); //圈出单行列零元素
void twozero(MATRIX &hungary); //圈出行列存在两个以上的零元素
void judge(MATRIX &hungary,int result[2000][2]); //判断是否符合匈牙利算法条件
void refresh(MATRIX &hungary); //不符合条件,对矩阵进行变形
void output(int result[2000][2],MATRIX hungary); //结果输出
MATRIX input(); //初始输入
int main()
{result[0][0]=0;hungary=input();zeroout(hungary);circlezero(hungary); output(result,hungary);
}
MATRIX input()
{int i,j; matrix hungary;printf("指派问题的匈牙利解法\n");printf("请输入cost矩阵的阶数:\n");scanf("%d",&hungary.matrixsize);printf("请输入代表工人和工件的%d阶矩阵:\n",hungary.matrixsize);for(i=1;i<=hungary.matrixsize;i++)for(j=1;j<=hungary.matrixsize;j++){ scanf("%d",&hungary.cost[i][j]);hungary.costforout[i][j]=hungary.cost[i][j];}return hungary;
}
void zeroout(MATRIX &hungary)
{int i,j; int tem; //表示同行的最大元素或同列的最大元素 for(i=1;i<=hungary.matrixsize;i++) //减去同行最大元素{ tem=hungary.cost[i][1];for(j=2;j<=hungary.matrixsize;j++)if(hungary.cost[i][j]tem=hungary.cost[1][j];for(i=2;i<=hungary.matrixsize;i++)if(hungary.cost[i][j]int i,j,p; int flag; for(i=0;i<=hungary.matrixsize;i++) //在矩阵外面构建半圈矩阵标记0的个数;hungary.cost[i][0]=0; for(j=1;j<=hungary.matrixsize;j++)hungary.cost[0][j]=0;for(i=1;i<=hungary.matrixsize;i++)for(j=1;j<=hungary.matrixsize;j++)if(hungary.cost[i][j]==0){hungary.cost[i][0]++;hungary.cost[0][j]++;hungary.cost[0][0]++;} for(i=0;i<=hungary.matrixsize;i++) //新建一个矩阵for(j=0;j<=hungary.matrixsize;j++) hungary.zeroelem[i][j]=0; flag=hungary.cost[0][0]+1; //flag = 0的总个数+1while(hungary.cost[0][0]flag=hungary.cost[0][0]; //行列单0的情况,for(i=1;i<=hungary.matrixsize;i++) //第一遍先行后列{if(hungary.cost[i][0]==1) {for(j=1;j<=hungary.matrixsize;j++) if(hungary.cost[i][j]==0&&hungary.zeroelem[i][j]==0)break;hungary.zeroelem[i][j]=1;hungary.cost[i][0]--;hungary.cost[0][j]--;hungary.cost[0][0]--;if(hungary.cost[0][j]>0)for(p=1;p<=hungary.matrixsize;p++)if(hungary.cost[p][j]==0&&hungary.zeroelem[p][j]==0){hungary.zeroelem[p][j]=2;hungary.cost[p][0]--;hungary.cost[0][j]--;hungary.cost[0][0]--;} } }for(j=1;j<=hungary.matrixsize;j++) // 第二遍先列后行{if(hungary.cost[0][j]==1){for(i=1;i<=hungary.matrixsize;i++)if(hungary.cost[i][j]==0&&hungary.zeroelem[i][j]==0)break;hungary.zeroelem[i][j]=1;hungary.cost[i][0]--;hungary.cost[0][j]--;hungary.cost[0][0]--;if(hungary.cost[i][0]>0)for(p=1;p<=hungary.matrixsize;p++)if(hungary.cost[i][p]==0&&hungary.zeroelem[i][p]==0){hungary.zeroelem[i][p]=2;hungary.cost[i][0]--;hungary.cost[0][p]--;hungary.cost[0][0]--;}}}}if(hungary.cost[0][0]>0)twozero(hungary);elsejudge(hungary,result);
}
void judge(MATRIX &hungary,int result[5041][2])
{int i,j;int num=0; //线的条数 int start; //每组解的储存开始位置 for(i=1;i<=hungary.matrixsize;i++)for(j=1;j<=hungary.matrixsize;j++)if(hungary.zeroelem[i][j]==1)num++; //划线的条数 if(num==hungary.matrixsize){start=result[0][0]*hungary.matrixsize+1;for(i=1;i<=hungary.matrixsize;i++)for(j=1;j<=hungary.matrixsize;j++)if(hungary.zeroelem[i][j]==1){result[start][0]=i;result[start++][1]=j;}result[0][0]++;}elserefresh(hungary);
}
void twozero(MATRIX &hungary)
{int i,j;int p,q;int m,n;int flag;MATRIX backup;for(i=1;i<=hungary.matrixsize;i++)if(hungary.cost[i][0]>0)break;if(i<=hungary.matrixsize){for(j=1;j<=hungary.matrixsize;j++){backup=hungary;//备份以寻找多解 if(hungary.cost[i][j]==0&&hungary.zeroelem[i][j]==0){hungary.zeroelem[i][j]=1;hungary.cost[i][0]--;hungary.cost[0][j]--;hungary.cost[0][0]--;for(q=1;q<=hungary.matrixsize;q++)if(hungary.cost[i][q]==0&&hungary.zeroelem[i][q]==0){hungary.zeroelem[i][q]=2;hungary.cost[i][0]--;hungary.cost[0][q]--;hungary.cost[0][0]--;}for(p=1;p<=hungary.matrixsize;p++)if(hungary.cost[p][j]==0&&hungary.zeroelem[p][j]==0){hungary.zeroelem[p][j]=2;hungary.cost[p][0]--;hungary.cost[0][j]--;hungary.cost[0][0]--;}flag=hungary.cost[0][0]+1;while(hungary.cost[0][0]flag=hungary.cost[0][0];for(p=i+1;p<=hungary.matrixsize;p++){if(hungary.cost[p][0]==1){for(q=1;q<=hungary.matrixsize;q++)if(hungary.cost[p][q]==0&&hungary.zeroelem[p][q]==0)break;hungary.zeroelem[p][q]=1;hungary.cost[p][0]--;hungary.cost[0][q]--;hungary.cost[0][0]--;for(m=1;m<=hungary.matrixsize;m++)if(hungary.cost[m][q]==0&&hungary.zeroelem[m][q]==0){hungary.zeroelem[m][q]=2;hungary.cost[m][0]--;hungary.cost[0][q]--;hungary.cost[0][0]--;}}}for(q=1;q<=hungary.matrixsize;q++){if(hungary.cost[0][q]==1){for(p=1;p<=hungary.matrixsize;p++)if(hungary.cost[p][q]==0&&hungary.zeroelem[p][q]==0)break;hungary.zeroelem[p][q]=1;hungary.cost[p][q]--;hungary.cost[0][q]--;hungary.cost[0][0]--;for(n=1;n<=hungary.matrixsize;n++)if(hungary.cost[p][n]==0&&hungary.zeroelem[p][n]==0){hungary.zeroelem[p][n]=2;hungary.cost[p][0]--;hungary.cost[0][n]--;hungary.cost[0][0]--;}}}}if(hungary.cost[0][0]>0) //确保hungary.cost[][]中的0元素都在zeroelem[][]中被完全标记出来。twozero(hungary);else judge(hungary,result);} hungary=backup;}}
}
void refresh(MATRIX &hungary)
{int i,j,min=0;int flag1=0,flag2=0;for(i=1;i<=hungary.matrixsize;i++){for(j=1;j<=hungary.matrixsize;j++)if(hungary.zeroelem[i][j]==1){hungary.zeroelem[i][0]=1; //有独立零元素break;}}while(flag1==0){flag1=1;for(i=1;i<=hungary.matrixsize;i++)if(hungary.zeroelem[i][0]==0){hungary.zeroelem[i][0]=2;for(j=1;j<=hungary.matrixsize;j++)if(hungary.zeroelem[i][j]==2){hungary.zeroelem[0][j]=1;}}for(j=1;j<=hungary.matrixsize;j++){if(hungary.zeroelem[0][j]==1){hungary.zeroelem[0][j]=2;for(i=1;i<=hungary.matrixsize;i++)if(hungary.zeroelem[i][j]==1){hungary.zeroelem[i][0]=0;flag1=0;}}}} //对打勾的行和列标记成2 for(i=1;i<=hungary.matrixsize;i++){if(hungary.zeroelem[i][0]==2){for(j=1;j<=hungary.matrixsize;j++){if(hungary.zeroelem[0][j]!=2)if(flag2==0){min=hungary.cost[i][j];flag2=1;}else{if(hungary.cost[i][j]if(hungary.zeroelem[i][0]==2)for(j=1;j<=hungary.matrixsize;j++)hungary.cost[i][j]=hungary.cost[i][j]-min;}for(j=1;j<=hungary.matrixsize;j++){if(hungary.zeroelem[0][j]==2)for(i=1;i<=hungary.matrixsize;i++)hungary.cost[i][j]=hungary.cost[i][j]+min;} //未被划线的行减去未被覆盖的最小值,被划线的列加上未被覆盖的最小值 for(i=0;i<=hungary.matrixsize;i++)for(j=0;j<=hungary.matrixsize;j++)hungary.zeroelem[i][j]=0; //矩阵清0circlezero(hungary);
}
void output(int result[5041][2],MATRIX hungary)
{int num; //解的数量 int minsum; //最小的工作成本 int i,j;char w;int start; //每个解的储存开始位置 minsum=0;for(i=1;i<=hungary.matrixsize;i++){minsum=minsum+hungary.costforout[i][result[i][1]];}printf("最优解的目标函数值为%d.\n",minsum);num=result[0][0];printf("有%d种解.\n",num); getchar();for(i=1;i<=num;i++){printf("按任意键输出第%d种解.\n",i);scanf("%c",&w);start=(i-1)*hungary.matrixsize+1; for(j=start;j
Assignment Problem(任务分配问题) 详解
Cmd Markdown 公式指导手册
十分钟教你求解分配问题
匈牙利算法详解