牛客练习赛17 E求长度 TSP+最短路

本文探讨了一个特定的图论问题:在给定的一幅图中找到一条从起点出发,经过指定的几个必经点后再返回起点的最短路径。通过使用SPFA算法预处理各必经点间的最短距离,并结合状态压缩动态规划求解最终答案。

链接:https://www.nowcoder.com/acm/contest/109/E
来源:牛客网

时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

给定一幅n个点m条边的图和S个一定要经过的点,问从0号点出发,经过这S个点再回到0号点的最短路径长度是多少。

输入描述:

第一行一个整数T(T <= 2)表示数据组数。
对于每组数据,第一行两个整数n,m表示点数和边数(1 <= n, m <= 100,000)。
接下来m行,每行三个整数x, y, z(0 < x, y < n, 0 <= z <= 1000)表示xy之间有一条长度为c的双向边;
接下来一个整数S。(S<=10)
接下来S行每行一个整数表示一定要经过的点。
数据保证有解。

输出描述:

T行,每行一个整数表示答案。
示例1

输入

1
4 6
0 1 1
1 2 1
2 3 1
3 0 1
0 2 5
1 3 5
3
1
2
3

输出

4
思路:

发现到 只有10,因此整张图有用的点只有不超过 个( 个点和 号点),因此我们先跑若干次最短路可以得到这些点两 两之间的最短路.
处理完以后我们可以全拍列枚举中间 个点的顺序来做
也可以用状压 来做 表示当前选的点的状态为s ,最后一步走到的位置是 j的情况下最短路径长度 然后我们可 以用 不 在s 内点 去更新dp[s][j]

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int INF=0x3f3f3f3f;
struct node{int to,w;};
typedef pair<int,int> P;
vector<node>edge[N];
int n,m,x,y,val,s;int vis[N],dis[12][N],S[11];
int ans[1<<(12)][12];
void spfa(int s,int pos){
	memset(vis,0,sizeof(vis));
	dis[pos][s]=0;
	priority_queue<P,vector<P>,greater<P> >q;
	q.push({0,s});
	while(!q.empty()){
		int u=q.top().second;q.pop();
		if(vis[u]) continue;vis[u]=1;//打上标记,说此时这个边已经是最短了 
		for(int i=0;i<edge[u].size();i++){
			int v=edge[u][i].to,w=edge[u][i].w;
			if(dis[pos][v]>dis[pos][u]+w){
				dis[pos][v]=dis[pos][u]+w;
				q.push({dis[pos][v],v});
			}
		}
	}
}
void init(){
	for(int i=0;i<n;i++) edge[i].clear();
}
int main(){
	int t;scanf("%d",&t);
	while(t--){
		init();
		memset(dis,0x3f,sizeof(dis));
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++){
			scanf("%d%d%d",&x,&y,&val);
			edge[x].push_back({y,val});
			edge[y].push_back({x,val});
		} 
		spfa(0,0);S[0]=0;
		scanf("%d",&s);
		for(int i=1;i<=s;i++){
			scanf("%d",&x);
			spfa(x,i);S[i]=x;
		}
		memset(ans,0x3f,sizeof(ans));ans[1][0]=0;
		for(int i=1;i<=(1<<(s+1));i++)
		for(int j=0;j<=s;j++){
			if(ans[i][j]!=INF){
				for(int k=0;k<=s;k++){
					if((i&(1<<k))==0&&dis[j][S[k]]!=INF){
						ans[i|(1<<k)][k]=min(ans[i|(1<<k)][k],ans[i][j]+dis[j][S[k]]);
					}
				}
			}	
		}
		int ans_=INF;
		int all=(1<<(s+1))-1;
		for(int i=0;i<=s;i++) ans_=min(ans_,ans[all][i]+dis[0][S[i]]);
		printf("%d\n",ans_); 
	}
	return 0;
}

  

转载于:https://www.cnblogs.com/vainglory/p/9011607.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值