TSP旅行商问题--位运算--Sabrina--Sabrinadol

TSP问题 (tsp.cpp/c/pas) Time Limit : 1 sec , Memory Limit : 128MB Description 对于给定的加权有向图G(V,E),查找满足以下条件的最短路径的距离:  这条路径是一个环,即这条路径的起点和终点都是同一个点。  每个顶点只能访问一次。 Input |V| |E| s0 t0 d0 s1 t1 d1 : s|E|−1 t|E|−1 d|E|−1 |V| 是顶点数,|E|是边数。顶点以数字 0, 1,…, |V|-1表示。 si 和ti 表示第i条边(有向)的源和目标顶点,di 表示si和 ti (第i条边)之间的距离。 Output 在一行中输出最短距离。如果没有解决方案,则输出 -1。 Constraints  2 ≤ |V| ≤ 15  0 ≤ di ≤ 1,000  没有多重边
Sample Input 1 4 6 0 1 2 1 2 3 1 3 9 2 0 1 2 3 6 3 2 4 Sample Output 1 16
Sample Input 2 3 3 0 1 1 1 2 1 0 2 1 Sample Output 2 -1

题解

1.首先看到本题,绝大多数人的想法都是DFS,虽然时间复杂度有点高,但是一看数据,又不太,只要我们在深搜中加上记忆化处理,顺便剪一剪枝,也是可以过的

但是!!

我想说的不是DFS,而是带上动规的思想。
如下

(1)(实现需要使用位运算,等会说)
首先假设出一个dp[s][k],s代表已经被扫到的元素的集合,k代表刚进入集合,这里我们是不是就需要列出和k相连的元素(i),然后判定该元素是否已经属于该集合,如果不,就向下扫取最小值
动规方程:
dp[s][i]=min{dp[s+i][i]+d[k][i]}//d数组存储的是图
(2)边界处理
1.记忆化处理,当我们扫到的dp值大于0的时候,说明该点已经被扫过了,直接返回值就好
2.当扫到初始点的时候,直接返回dp=0就好了
(3)元素属于判断以及判断是否扫完每一个元素
首先我们知道每种元素只有在集合中和不在集合中两种状态,所以我们可以依靠二进制位运算来表示每个元素的状态,0为不在,1为在

插入:位运算

其实一共就是五种
通过二进制位来实现
1.&:与 两个二进制位都为1才1
2.|:或 两个二进制位只要有一个为1就为1
3.^:异或 相同为1,相反为0
4.<<左移 将整个数的二进制位左移 右补0 左舍
5.>> 右移 溢出补0 左补0 右舍
这篇文章写得通俗易懂

(4)代码,有详细注释

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#define maxn 99999
using namespace std; 
int moving[1<<16][100];
int a[1000][1000];
int b,c,d;
int n,m;
int search(int s,int k)
{
	if(moving[s][k]>=0) //这里就是上面所说的记忆化
	return moving[s][k];
	if(s==(1<<n)-1&&k==0)//如果s已满那么加一就会进位到2的n次方,k等于代表回到起点
	return moving[s][k]=0;//已经回到了,不再有值
	int num=9999;
	for(int i=0;i<n;i++)
	{
		if(!(s>>i&1))//先判定i点是否加入了集合
		{
			num=min(num,search(s|1<<i,i)+a[k][i]);
			//如果没加入集合就取小,这里因为我们将a数组初始化得大于最大值,所以不用考虑能否到达
		}
	}
	return moving[s][k]=num;
}
int main()
{
	//freopen("tsp.in","r",stdin);
	//freopen("tsp.out","w",stdout);
	memset(moving,-1,sizeof(moving));
	memset(a,0x3f,sizeof(a));
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		cin>>b>>c>>d;
		a[b][c]=d;
	}
	long long int kk=search(0,0);
	if(kk==9999||kk<0)
		cout<<"-1";//如果值未更新的话,就相当没有找到
	else
		cout<<kk;
	while(1)
	cout<<"love Sabrina"<<endl;//防copy
	
		fclose(stdin);
		fclose(stdout);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于2-opt的算法是一种常用的旅行商问题(Traveling Salesman Problem,TSP)的启发式算法。下面是基本的2-opt算法TSP问题的步骤: 1. 初始化: - 随机选择一个初始路径作为旅行商的遍历顺序。 2. 2-opt操作: - 对当前路径中的每一对边进行检查,判断是否存在更短的路径。 - 对于路径上的每一对边 (i, j) 和 (k, l),计算两种交换方式后的路径长度: - 交换(i, j)和(k, l)之间的边得到新路径。 - 交换(i, k)和(j, l)之间的边得到新路径。 - 如果存在交换可以使路径变短,则选择变短幅度最大的交换方式,并更新路径。 3. 终止条件: - 如果经过一次完整的2-opt操作后,路径没有变化或者变化很小,则认为找到了近似最优解,终止算法。 - 否则,返回步骤2继续进行2-opt操作。 通过不断地进行2-opt操作,可以不断优化路径,使其逐渐接近TSP问题的最优解。2-opt算法的时间复杂度较低,但并不能保证找到全局最优解,只能得到一个较好的近似解。为了进一步提高结果质量,可以使用更复杂的启发式算法、局部搜索策略等来改进解过程。 需要注意的是,2-opt算法适用于小规模的TSP问题,对于大规模问题,可能需要结合其他算法或者优化技术来解。此外,对于特定的TSP问题,还可以根据问题特点设计相应的启发式规则和策略,以提高解效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值