运筹学学生学区最小距离问题需要求解任意两个节点之间的最短距离。只要求解单源最短路径问题,就可以知道运筹学学生学区最小距离。
为济源等地区用户提供了全套网页设计制作服务,及济源网站建设行业解决方案。主营业务为网站设计制作、成都网站建设、济源网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
#include stdio.h
#include stdlib.h
#define MAX 100
#define STP 100
int stop=1; //迭代记数变量
int status; //iterative迭代返回值:1唯一最优,0无界解,-1无穷多最优解 -2迭代超过限制次数
int step=1; //目前阶段
double a[MAX][MAX],b[MAX],c[MAX],temp_c[MAX],max=0; //方程组相关系数
int num_x; //变量个数
int num_st; //约束方程数
int num_ar=0; //人工变量个数
int arti[MAX]; //人工变量下标
int base[MAX]; //基变量下标
int ma_mi; //1为求最大值,2为求最小值
void create(); //建立方程组
void iterative(); //单纯型法迭代
void output(); //输出结果
void banner(); //打印程序标题
void exchange(); //交换两阶段价值系数
void show(); //输出方程组
void main() {
int i,j,k;
banner();
create();
//保存原价值系数,转换为第一阶段价值系数
for(i=1;i=num_x;i++) {
k=0;
for(j=1;j=num_ar;j++) if(i==arti[j]) k=1;
if(k==1) temp_c=-1;
else temp_c=0;
}
exchange(c,temp_c);
printf("\n\n第一阶段问题为:\n\n");
show();
step++;
printf("\n\n按回车开始第一阶段迭代");
getchar();
getchar();
iterative();
if(status==-2) {
puts("迭代超过限制次数强行终止!\n");
puts("\n按回车结束");
getchar();
exit(0);
}
output();
if(max!=0) {
puts("\n\n原问题无可行解。\n");
puts("\n按回车结束");
getchar();
exit(0);
}
//转换为第二阶段价值系数
exchange(c,temp_c);
//把人工变量列全设为0
for(i=1;i=num_ar;i++) {
c[arti]=0;
for(j=1;j=num_st;j++) a[j][arti]=0;
}
puts("\n\n第二阶段问题为:\n\n");
show();
puts("\n\n按回车开始第二阶段迭代");
getchar();
iterative();
switch(status) {
case 1:
output();
puts("\n\n原问题有唯一最优解。\n");
puts("\n按回车结束");
getchar();
exit(0);
case 0:
puts("\n\n原问题为无界解。\n");
puts("\n按回车结束");
getchar();
exit(0);
case -1:
output();
puts("\n\n原问题有无穷多最优解。\n");
puts("\n按回车结束");
getchar();
exit(0);
case -2:
puts("迭代超过限制次数强行终止!\n");
puts("\n按回车结束");
getchar();
exit(0);
}//switch
}
void banner() {
printf("\t\t****************************************\n");
printf("\t\t 单纯型法解线性规划问题\n");
printf("\t\t 作者:Thunder\n");
printf("\t\t****************************************\n");
printf("\n");
}
void show() {
//对方程组以自然的格式输出,系数为零的x不显示
//为1的不显示系数1,-1系数只显示负号
int i,j,k;
switch(step) {
case 1:
printf("min z= ");
printf("x[%d]",arti[1]);
for(i=2;i=num_ar;i++) printf(" + x[%d]",arti);
break;
case 2:
printf("max z= ");
printf("%lg x[%d]",c[1],1);
for(i=2;i=num_x;i++) {
if(c==1) printf(" + x[%d]",i);
else if(c==-1) printf(" - x[%d]",i);
else if(c=0) printf(" +%lg x[%d]",c,i);
else printf(" %lg x[%d]",c,i);
}
break;
}
printf("\nst:\n");
for(i=1;i=num_st;i++) {
k=0;
for(j=1;j=num_x;j++) {
if(a[j]!=0) {
if(a[j]==1k!=0) printf(" + x[%d]",j);
else if(a[j]==1k==0) printf(" x[%d]",j);
else if(a[j]==-1) printf(" - x[%d]",j);
else if(a[j]=0k!=0) printf(" +%lg x[%d]",a[j],j);
else if(a[j]=0k==0) printf(" %lg x[%d]",a[j],j);
else printf(" %lg x[%d]",a[j],j);
k=1;
}
}
printf(" == %lg\n",b);
}
printf(" x[1]~x[%d]=0",num_x);
}
void exchange() {
int i;
double temp[MAX];
for(i=1;i=num_x;i++) {
temp=temp_c;
temp_c=c;
c=temp;
}
}
void create() {
//输入方程组系数,每个方程输完后回显确认
int i,j,k,re_st[MAX],tnum_x,num_addv=0,num_ba=0;
char confirm;
while(1) {
printf("请选择:1、求最大值,2、求最小值:(1/2)");
scanf("%d",ma_mi);
if(ma_mi!=1ma_mi!=2) printf("输入错误,重新选择。");
else break;
}
while(1) {
printf("指定变量个数:");
scanf("%d",num_x);
printf("输入价值系数c1-c%d:\n",num_x);
for(i=1;i=num_x;i++) {
printf("c%d=",i);
scanf("%lf",c);
}
if(ma_mi==1) printf("max z= ");
else printf("min z= ");
printf("%lg x[%d]",c[1],1);
for(i=2;i=num_x;i++) {
if(c=0) printf(" +%lg x[%d]",c,i);
else printf(" %lg x[%d]",c,i);
}
printf("\n正确吗?:(y/n)");
getchar();
confirm=getchar();
if (confirm=='y') break;
else if(confirm=='n') continue;
}
printf("输入约束方程组个数:");
scanf("%d",num_st);
for(i=1;i=num_st;i++) {
printf("st.%d:\n",i);
while(1) {
printf("请选择:1、==,2、=,3、= :(1/2/3)");
scanf("%d",re_st);
if(re_st!=1re_st!=2re_st!=3) printf("输入错误,请重新选择。");
else break;
}
printf("输入技术系数:\n");
for(j=1;j=num_x;j++) {
printf("a%d=",j);
scanf("%lf",a[j]);
}
printf("输入资源拥有量:\nb%d=",i);
scanf("%lf",b);
printf("st.%i:\n",i);
printf("%lg x[%d]",a[1],1);
for(j=2;j=num_x;j++) {
if(a[j]=0) printf(" +%lg x[%d]",a[j],j);
else printf(" %lg x[%d]",a[j],j);
}
switch(re_st) {
case 1: printf(" == %lg",b); break;
case 2: printf(" = %lg",b); break;
case 3: printf(" = %lg",b); break;
}
while(1) {
printf("\n正确吗?(y/n)");
getchar();
confirm=getchar();
if (confirm=='y') break;
else if(confirm=='n') {i-=1; break;}
}
}
//显示输入的方程组
printf("\n原问题为:\n\n");
if(ma_mi==1) printf("max z= ");
else printf("min z= ");
printf("%lg x[%d]",c[1],1);
for(i=2;i=num_x;i++) {
if(c==1) printf(" + x[%d]",i);
else if(c==-1) printf(" - x[%d]",i);
else if(c=0) printf(" +%lg x[%d]",c,i);
else printf(" %lg x[%d]",c,i);
}
printf("\nst:\n");
for(i=1;i=num_st;i++) {
k=0;
for(j=1;j=num_x;j++) {
if(a[j]!=0) {
if(a[j]==1k!=0) printf(" + x[%d]",j);
else if(a[j]==1k==0) printf(" x[%d]",j);
else if(a[j]==-1) printf(" - x[%d]",j);
else if(a[j]=0k!=0) printf(" +%lg x[%d]",a[j],j);
else if(a[j]=0k==0) printf(" %lg x[%d]",a[j],j);
else printf(" %lg x[%d]",a[j],j);
k=1;
}
}
switch(re_st) {
case 1:
printf(" == %lg\n",b);
break;
case 2:
printf(" = %lg\n",b);
break;
case 3:
printf(" = %lg\n",b);
break;
}
}
printf(" x[1]~x[%d]=0\n",num_x);
tnum_x=num_x;
for(i=1;i=num_st;i++) {
switch(re_st) {
case 1:
case 3:
num_x+=1;
break;
case 2:
num_x+=2;
break;
}
}
//化为标准形式
if(ma_mi==2) for(i=1;i=tnum_x;i++) c*=-1; //求最小值时,系数变相反数
for(i=1;i=num_st;i++) {
switch(re_st) {
case 1:
num_addv++;
num_ba++;
num_ar++;
c[tnum_x+num_addv]=0;
base[num_ba]=arti[num_ar]=tnum_x+num_addv;
for(j=tnum_x+1;j=num_x;j++)
if(j==tnum_x+num_addv) a[tnum_x+num_addv]=1;
else a[j]=0;
break;
case 2:
num_addv++;
c[tnum_x+num_addv]=0;
num_addv++;
num_ba++;
num_ar++;
c[tnum_x+num_addv]=0;
base[num_ba]=arti[num_ar]=tnum_x+num_addv;
for(j=tnum_x+1;j=num_x;j++)
if(j==tnum_x+num_addv-1) a[tnum_x+num_addv-1]=-1;
else if(j==tnum_x+num_addv) a[tnum_x+num_addv]=1;
else a[j]=0;
break;
case 3:
num_addv++;
num_ba++;
c[tnum_x+num_addv]=0;
base[num_ba]=tnum_x+num_addv;
for(j=tnum_x+1;j=num_x;j++)
if(j==tnum_x+num_addv) a[tnum_x+num_addv]=1;
else a[j]=0;
break;
}//switch
}//增加松弛变量、剩余变量、人工变量、确定基变量
//显示标准化后的方程组
printf("\n化为标准形式后:\n\n");
if(ma_mi==1) printf("max z= ");
else printf("max z'= ");
printf("%lg x[%d]",c[1],1);
for(i=2;i=num_x;i++) {
k=0;
for(j=1;j=num_ar;j++)
if(i==arti[j]) k=1;
if(k==1) printf(" -M x[%d]",i);
else if(c==1) printf(" + x[%d]",i);
else if(c==-1) printf(" - x[%d]",i);
else if(c=0) printf(" +%lg x[%d]",c,i);
else printf(" %lg x[%d]",c,i);
}
printf("\nst:\n");
for(i=1;i=num_st;i++) {
k=0;
for(j=1;j=num_x;j++) {
if(a[j]!=0) {
if(a[j]==1k!=0) printf(" + x[%d]",j);
else if(a[j]==1k==0) printf(" x[%d]",j);
else if(a[j]==-1) printf(" - x[%d]",j);
else if(a[j]=0k!=0) printf(" +%lg x[%d]",a[j],j);
else if(a[j]=0k==0) printf(" %lg x[%d]",a[j],j);
else printf(" %lg x[%d]",a[j],j);
k=1;
}
}
printf(" == %lg\n",b);
}
printf(" x[1]~x[%d]=0",num_x);
}
void iterative() {
int i,j,k,k_a,k_f,l; //k_a,k_f值为0或1,记录当前下标在arti[]或base[]里的搜索结果
int base_elem;
int base_out,base_in;
double sigma[MAX],temp;
double value_be; //高斯消元里保存主元素值
printf("\n\n第%d次迭代:\n\n",stop);
for(i=1;i=num_st;i++) {
printf("c%d=%lg\t",base,c[base]);
printf("b%d=%lg\t",i,b);
switch(step) {
case 1:
for(j=1;j=num_x;j++)
{
printf("a[%d][%d]=%lg\t",i,j,a[j]);
}
printf("\n");
break;
case 2:
for(j=1;j=num_x;j++) {
k_a=0;
for(l=1;l=num_ar;l++) if(j==arti[l])k_a=1;
if(k_a!=1) printf("a[%d][%d]=%lg\t",i,j,a[j]);
}
printf("\n");
break;
}
}
//求检验数sigma
for(i=1;i=num_x;i++) {
sigma=c;
for(j=1;j=num_st;j++) sigma-=c[base[j]]*a[j];
for(j=1;j=num_st;j++) if(i==base[j]) sigma=0;
switch(step) {
case 1:
printf("sigma[%d]=%lg\t",i,sigma);
break;
case 2:
k_a=0;
for(l=1;l=num_ar;l++) if(i==arti[l]) k_a=1;
if(k_a!=1) printf("sigma[%d]=%lg\t",i,sigma);
break;
}
}
putchar('\n');
//检验检验数sigma是否全小于等于0
k=0;
for(i=1;i=num_x;i++) {
if(sigma0)
k=1;
}
if(k==0) {
//sigma是全小于等于0时,检查是否为无穷多最优解
for(i=1;i=num_x;i++) {
k_f=k_a=0;
for(j=1;j=num_ar;j++)
if(i==arti[j]) k_a=1;
if(sigma==0k_a!=1) {
for(j=1;j=num_st;j++) if(i==base[j]) k_f=1;
if(k_f==0) {status=-1; return;}
}
}
status=1;
return;
}
//检查是否为无界解
for(i=1;i=num_x;i++) {
k_f=0;
if(sigma0) {
for(j=1;j=num_st;j++) if(a[j]0) k_f=1;
if(k_f!=1) {status=0; return;}
}
}
//确定换入变量
for(i=1;i=num_x;i++) {
k=0;
for(j=1;j=num_st;j++) if(i==base[j]) k=1;
if(k==0sigma0) temp=sigma-1;
}//temp赋初值
for(i=1;i=num_x;i++) {
k=0;
for(j=1;j=num_st;j++) if(i==base[j]) k=1;
if(k==0)
if(sigmatempsigma0) {
base_in=i;
temp=sigma;
}
}
//确定换出变量
for(i=1;i=num_st;i++)
if(a[base_in]0) {
temp=b/a[base_in]+1;
break;
}//temp赋初值
for(i=1;i=num_st;i++) {
if(b/a[base_in]=tempa[base_in]0) {
for(j=1;j=num_ar;j++)
if(base==arti[j]) {
base_out=base;
base_elem=i;
temp=b/a[base_in];
break;
}
}//人工变量优先换出
if(b/a[base_in]tempa[base_in]0) {
base_out=base;
base_elem=i;
temp=b/a[base_in];
}
}
printf(" 基变量:");
for(i=1;i=num_st;i++) printf("x[%d] ",base);
printf("换入变量:x[%d] 换出变量:x[%d]",base_in,base_out);
//基变量变换,进行新方程初始化后迭代
for(i=1;i=num_st;i++) {
if(base==base_out) base=base_in;
}
//初始化主元素行系数
value_be=a[base_elem][base_in];
b[base_elem]/=value_be;
for(i=1;i=num_x;i++) a[base_elem]/=value_be;
for(i=1;i=num_st;i++) {
if(i!=base_elem) {
b-=b[base_elem]*a[base_in];
value_be=a[base_in];
for(j=1;j=num_x;j++) a[j]-=a[base_elem][j]*value_be;
}
}
stop++;
if(stopSTP) {status=-2; return;}
iterative();
}
void output() {
int i,j;
double X[MAX];
printf("\n结果如下:\n");
printf("\nX=(");
for(i=1;i=num_x;i++) {
for(j=1;j=num_st;j++)
if(i==base[j]) {X=b[j];break;}
else X=0;
printf("%lg ",X);
}
printf(")");
for(i=1;i=num_x;i++) max+=c*X;
if(ma_mi==1) printf("\nMax z= %lf\n",max);
else printf("\nMin z= %lf\n",-max);
}
public class AssignWorkProblem {
public static void main(String[] args) {
/*
*测试
**/
int[][] cost=new int[][]{{2,15,13,4},{10,4,14,15},{9,14,16,13},{7,8,11,9}};
System.out.println(Arrays.toString(awpProcedure(cost, 4, 4)));
cost=new int[][]{{12,7,9,7,9},{8,9,6,6,6},{7,17,12,14,12},{15,14,6,6,10},{4,10,7,10,6}};
System.out.println(Arrays.toString(awpProcedure(cost, 5, 5)));
}
/*
* 费用矩阵costMatrix,由于要改变costMatrix的值,clone方法只能对基本类型;
* pnum即为几个人,也是costMatrix的行数,wnum是几个任务,也是costMatrix的列数
* 返回值:没两个数字为一组,表示i,j。如返回[1,1,2,0]表示costMatrix[1][1]、costMatrix[2][0]费用最低
*/
public static int[] awpProcedure(int[][] costMatrix,int pnum,int wnum){
if(pnum1||pnumwnum)
return null; //test n=m
int[][] costC=new int[pnum][]; //clone 一份costMatrix
for(int i=0;ipnum;i++){
costC[i]=costMatrix[i].clone();
}
//每行减去最小的元素
int[] lzeroa=new int[pnum+1]; //记录每行0的个数,lzero[pnum]记录0最少的行标
lzeroa[pnum]=-1;
int i,j;
for(i=0;ipnum;i++){
int lmin=costC[i][0]; //记录每行最小的
for(j=1;jwnum;j++)
lmin=lmincostC[i][j]?costC[i][j]:lmin;
for(j=0;jwnum;j++){
costC[i][j]-=lmin;
lzeroa[i]+=costC[i][j]==0?1:0;
}
}
for(j=0;jwnum;j++){
int cmin=costC[0][j]; //记录每列最小的
for(i=1;ipnum;i++)
cmin=cmincostC[i][j]?costC[i][j]:cmin;
if(cmin==0)continue;
for(i=0;ipnum;i++){
costC[i][j]-=cmin;
lzeroa[i]+=costC[i][j]==0?1:0;
}
}
int[] rzerop;
int whilenum=0;
while(true){
boolean[] lzerob=new boolean[pnum]; //记录某行是否查找过
rzerop=new int[pnum*2]; //记录0元素所在的行列
Arrays.fill(rzerop, -1);
if(awpIsSolution(costC,pnum,wnum,lzeroa.clone(),lzerob,rzerop))
break;
//下面调整矩阵
int[] coverLC=new int[pnum+wnum]; //要被标记的行列,0-pnum-1为行,pnum以后为列
Arrays.fill(coverLC, -1);
//没有找到合适0元素的行做标记
for(i=0;ipnum;i++)
if(lzerob[i]==false)coverLC[i]=i;
//对已经标记的行上的0元素所在的列做标记
for(i=0;ipnum;i++)
if(coverLC[i]!=-1){
for(j=0;jwnum;j++){
if(costC[coverLC[i]][j]==0)
coverLC[pnum+j]=j;
}
}
//对已经标记的列上的已经选中的0元素所在的行做标记
for(j=0;jwnum;j++){
if(coverLC[pnum+j]!=-1){
for(i=0;irzerop.lengthrzerop[i]!=-1;i+=2){
if(rzerop[i+1]==j)
coverLC[rzerop[i]]=rzerop[i];
}
}
}
//确定能找出新最小值的区域,直线覆盖掉没有打勾的行,打勾的列,最终coverLC[x]!=-1就是能选择的数
for(i=0;iwnum;i++){
if(coverLC[pnum+i]!=-1)coverLC[pnum+i]=-1;
else coverLC[pnum+i]=i;
}
//从区域中找出最小元素
int nmin=-1;
for(i=0;ipnum;i++){
if(coverLC[i]==-1)continue;
for(j=0;jwnum;j++){
if(coverLC[pnum+j]==-1)continue;
if(nmin==-1)nmin=costC[i][j];
else nmin=nmincostC[i][j]?costC[i][j]:nmin;
}
}
//打勾的列加上nmin,打勾的行减去nmin,记录0个数的数组作相应变化
for(j=0;jwnum;j++){
if(coverLC[pnum+j]==-1){
for(i=0;ipnum;i++){
if(costC[i][j]==0)lzeroa[i]-=1;
costC[i][j]+=nmin;
}
}
}
for(i=0;ipnum;i++){
if(coverLC[i]!=-1){
for(j=0;jwnum;j++){
costC[i][j]-=nmin;
if(costC[i][j]==0)lzeroa[i]+=1;
}
}
}
whilenum++;
if(whilenum==100){
System.out.println("100次之内矩阵调整没有找到");
return null;
}
}
return rzerop;
}
/*
* 测试矩阵costC是否有解,已经通过变换或者调整得到的矩阵
*/
public static boolean awpIsSolution(int[][] costC,int pnum,int wnum,int[] lzeroa,boolean[] lzerob,int[] rzerop){
int i,j,rzeropi=0;
for(int p=0;ppnum;p++){ //开始按照匈牙利法划去0所在的行列
//查找0元素个数最少的行
for(i=0;ipnum;i++){
if(lzerob[i]||lzeroa[i]1)continue; //如果某行已经查找过或者没有0元素,可能被划去了
if(lzeroa[pnum]!=-1lzeroa[i]lzeroa[lzeroa[pnum]])lzeroa[pnum]=i;
else if(lzeroa[pnum]==-1) lzeroa[pnum]=i;
}
//没有找到足够的不在同一行同一列的0元素,需要对矩阵进行调整,如果lzeroa[pnum]有值,则说明该行一定能找到
if(lzeroa[pnum]==-1){
return false;
}
//划去找到的行中没有被覆盖的0元素所在的行列
for(j=0;jwnum;j++){
if(costC[lzeroa[pnum]][j]!=0)continue;
//第一次找0元素最少的行
if(rzeropi==0){
rzerop[rzeropi++]=lzeroa[pnum];
rzerop[rzeropi++]=j;
lzerob[lzeroa[pnum]]=true; //找到第lzeroa[pnum]行,第j列0元素
//划去所在的行列时 lzeroa做相应的变化
for(i=0;ipnum;i++){
if(i!=lzeroa[pnum]costC[i][j]==0)
lzeroa[i]-=1;
}
lzeroa[pnum]=-1;
break;
}
//找到的0元素是否被划去
for(i=0;irzeropi(lzeroa[pnum]!=rzerop[i]j!=rzerop[i+1]);i+=2);
//如果被划去则找该行下一个0元素
if(irzeropi)continue;
rzerop[rzeropi++]=lzeroa[pnum];
rzerop[rzeropi++]=j;
lzerob[lzeroa[pnum]]=true;
for(i=0;ipnum;i++){
if(i!=lzeroa[pnum]costC[i][j]==0)
lzeroa[i]-=1;
}
lzeroa[pnum]=-1;
break;
}
}
return true;
}
}
运筹学软件可以选择韩伯棠教授的管理运筹学2.5软件,在管理运筹学书本附的光盘里,软件里只需要输入相应的系数就可以求出最后的解。
也可以选择lingo软件,需要输入目标函数和限制条件,相对输入复杂些,但计算结果一样,网上很多免费的。