Clarke & Wight saving algorithm求解tsp问题

Clarke & Wight saving algorithm求解tsp问题

//应用Clarke & Wight saving algorithm (c-w节约算法)求解旅行商问题(tsp)
//参考运筹学第三版pdf 17.2.2
#include<cstdio>
#include<cmath> 
#include<cstring>
double x[31]= {0},y[31]= {0};
long long d[500]= {0},e[500]= {0},s[31][61]= {0},route[31];
int i,j,h=1,n=0,l,o;
double a[31]= {0},b[31][31]= {0},c[31][31]= {0},f[500]= {0},k;
double sx,sy;//基点 
int posj1,posj2,posk1,posk2;
double sum=0;

double rad(double d) {	// 计算弧度
	const double PI=3.1415926535898;
	return d*PI/180.0;
}

double CalcDistance(double fLati1,double fLong1,double fLati2,double fLong2) {// 已知两个gps坐标点(纬度:fLati,经度:fLong)获得两点的直线距离,单位是千米
	const float EARTH_RADIUS=6378.137;
	double radLat1=rad(fLati1);
	double radLat2=rad(fLati2);
	double a=radLat1-radLat2;
	double b=rad(fLong1)-rad(fLong2);
	double s_=2*asin(sqrt(pow(sin(a/2),2)+cos(radLat1)*cos(radLat2)*pow(sin(b/2),2)));
	s_=s_*EARTH_RADIUS;
//	s = (int)(s*10000000) / 10000;//转换为m 
	return s_;//单位:km 
}

int main() {
	printf("C-W节约算法求解TSP问题 \n");
    printf("请输入坐标(30个以内),坐标之间用空格隔开,按回车键结束输入:\n");//坐标格式:(经度,纬度)
	scanf("%lf,%lf",&sx,&sy);
    while(~scanf("%lf,%lf",&x[h],&y[h])) {
		h++;
	}//h为总共点数(输入h-1个点)
//	printf("\nsx=%lf,sy=%lf\n",sx,sy);
//	printf("h=%d\n",h); 
//	for( i=1;i<h;++i) {
//		printf("x[%d]=%lf,y[%d]=%lf\n\n",i,x[i],i,y[i]);
//	}

    for(i=1; i<h; i++) {
		a[i]=CalcDistance(sy,sx,y[i],x[i]); 
//        printf("a[%d]=%lf\n",i,a[i]);
	}

    for(i=1; i<h-1; i++) {
        for(j=i+1; j<h; j++) {
            n++;
			b[i][j]=CalcDistance(y[i],x[i],y[j],x[j]); //b[][] 为距离矩阵
            c[i][j]=a[i]+a[j]-b[i][j];//c[][] 为添加边(i,j)的路程节约值
            d[n]=i;//d[]为索引n对应c[][]的横坐标 
            e[n]=j;//e[]为索引n对应c[][]的纵坐标 
            f[n]=c[i][j];//f[]为索引n对应的路程节约值   //每个n对应一个点对(i,j)且j>i,如(1,2)、(1,3)、...、(1,29)、(2,3)、(2,4)、...、(2,29)、...(28,29) 
//            printf("c[%d][%d]=%lf\n",i,j,c[i][j]); 
        }
    }
//    printf("b[][]:\n");
//    for(i=1;i<h;++i) {
//    	for(j=1;j<h;++j) {
//    		printf("%lf ",b[i][j]);
//		}
//		printf("\n");
//	}
//    printf("n=%d\n",n);n=h-2+h-3+...+1

    for(i=1; i<n; i++) {//按照f[]大小升序对d[]、e[]、f[]排序 
        for(j=1; j<n-i+1; j++)
            if(f[j]>f[j+1]) {
                k=f[j];
                f[j]=f[j+1];
                f[j+1]=k;
                l=d[j];
                d[j]=d[j+1];
                d[j+1]=l;
                o=e[j];
                e[j]=e[j+1];
                e[j+1]=o;
            }
    }
    printf("\n");
//    printf("节约值排序:\n");
//    for(i=n; i>0; i--) {
//        if(f[i]!=0){
//        	printf("%d,%d\n",d[i],e[i]);
//        	printf("%lf(%lf,%lf)--(%lf,%lf)\n",f[i],x[d[i]],y[d[i]],x[e[i]],y[e[i]]);
//		}
//    }
//    for(int i=n-1;i>0;--i) {
//    	printf("d[%d]=%d,e[%d]=%d\n",i,d[i],i,e[i]);

	for(i=1;i<h;++i) {//基点索引为0 s[][]每一行为一个环0->xxx->...->0 
		s[i][1]=0;//由基点发出 
		s[i][2]=i;//初始除基点外每个环只有1个点 
	}
// 	for(i=1; i<h; i++) {
//    	for(j=1;j<h;++j)
//    		if(s[i][j]!=0) {
//    			printf("s[%d][%d]=%ld ",i,j,s[i][j]);
//			}
//    	printf("\n");
//		for(j=1;j<h;++j)
//    		if(s[i][j]!=0) {
//    			printf("x[s[i][j]]=%lf,y[s[i][j]]=%lf",x[s[i][j]],y[s[i][j]]);;
//			}
//		printf("\n");       	
//    }
	for(i=n;i>0;--i) {//从n开始,对于每组点对贪心的选取点使其加入后较其他点节约值最大 
		for(j=1;j<h;++j) {
			for(int k=1;k<h;++k) {
				if(s[j][k]==0&&k!=1) break;
				if(s[j][k]==d[i]) {
					posj1=j;
					posk1=k;
				}
				if(s[j][k]==e[i]) {
					posj2=j;
					posk2=k;
				}
			}
		}
		if(posj1==posj2) {
			
		} else if(s[posj1][posk1-1]==0&&s[posj2][posk2+1]==0) {
			for(int l=1;l<=h;++l) {
				s[posj2][posk2+l]=s[posj1][posk1+l-1];
			}	
			memset(s[posj1],0,sizeof(s[posj1]));
		}else if(s[posj1][posk1+1]==0&&s[posj2][posk2-1]==0) {
			for(int l=1;l<=h;++l) {
				s[posj1][posk1+l]=s[posj2][posk2+l-1];
			}	
			memset(s[posj2],0,sizeof(s[posj2]));
		}else if(s[posj1][posk1+1]==0&&s[posj2][posk2+1]==0) {
			for(int l=1;l<=h&&posk2-l+1;++l) {
				s[posj1][posk1+l]=s[posj2][posk2-l+1];
			}	
			memset(s[posj2],0,sizeof(s[posj2]));
			sum-=f[i];
		}else if(s[posj1][posk1-1]==0&&s[posj2][posk2-1]==0) {
			int len=0;
			for(int l=1;l<=h;++l) {
				if(s[posj1][l]) 
					len++;
			}
			int t=0;
			int r=len-1+1;
			while(t<r){
				int temp;
				temp=s[posj1][t+1];
				s[posj1][t+1]=s[posj1][r+1];
				s[posj1][r+1]=temp;
				t++;
				r--;
			}
			for(int l=1;l<=h;++l) {
				s[posj1][posk1+l]=s[posj2][posk2+l-1];
			}	
			memset(s[posj2],0,sizeof(s[posj2]));
		}
		for(int i=1; i<=h; i++) {
	    	for(int j=1;j<=h;++j)
	    		if(s[i][j]!=0) {
	    			printf("s[%d][%d]=%ld  ",i,j,s[i][j]);
				}
	    	printf("\n");   	
	    } 
	}
//	for(i=1; i<h; i++) {
//    	for(j=1;j<h;++j)
//    		if(s[i][j]!=0) {
//    			printf("s[%d][%d]=%ld  ",i,j,s[i][j]);
//			}
//    	printf("\n");
//		for(j=1;j<h;++j)
//    		if(s[i][j]!=0) {
//    			printf("x[s[i][j]]=%lf,y[s[i][j]]=%lf  ",x[s[i][j]],y[s[i][j]]);;
//			}
//		printf("\n");       	
//    }
    printf("\n旅行商路线:\n");
    printf("数据中心(%lf,%lf)--",sx,sy);
    int num=0;
    for(i=1; i<=h; i++) {
    	for(j=1;j<=h;++j)
        	if(s[i][j]!=0) {
        		printf("传感器%d(%lf,%lf)--",s[i][j],x[s[i][j]],y[s[i][j]]);
        		route[++num]=s[i][j];//route[]为最终路线 
			}
    }
    printf("数据中心(%lf,%lf)",sx,sy);

    for(int i=1;i<num;++i) {
    	if(route[i]<route[i+1])
    		sum+=b[route[i]][route[i+1]];
    	else 
    		sum+=b[route[i+1]][route[i]];
	}
	sum+=a[route[1]]+a[route[num]]; 
    printf("总距离:%lf\n",sum);
	return 0;
}

//数据:经度,纬度 单位:度 
//第一个为数据中心坐标,后29个是传感器坐标 
//求从数据中心出发,经过所有点后回到数据中心的最短路线 
/* 
120.7015202,36.37423
120.6987175,36.37457569
120.6997954,36.37591239
120.70691,36.37579616
120.7056165,36.37248342
120.7031731,36.37753964
120.6928965,36.37800457
120.6943337,36.37521499
120.6973521,36.37876006
120.6962022,36.37643544
120.7011609,36.37905063
120.6939026,36.38021291
120.6983582,36.38056159
120.7025263,36.38120084
120.6914592,36.38201441
120.6960585,36.38247931
120.7005141,36.38276987
120.6998673,36.37079794
120.6928965,36.37079794
120.6964897,36.36824059
120.6969209,36.37143727
120.7052571,36.36899618
120.7088504,36.37021674
120.7087066,36.36731063
120.7130185,36.36829872
120.6896626,36.36661314
120.6937588,36.36242812
120.6993643,36.38741865
120.7129466,36.37201847
120.7002266,36.36428816
*/
//输入完按ctrl+z 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值