【全排列】【最短路】最短路(path)

D e s c r i p t i o n Description Description

给定一个n个点m条边的有向图,有k个标记点,要求从规定的起点按任意顺序经过所有标记点到达规定的终点,问最短的距离是多少。

I n p u t Input Input

第一行5个整数n、m、k、s、t,表示点个数、边条数、标记点个数、起点编号、终点编号。
接下来m行每行3个整数x、y、z,表示有一条从x到y的长为z的有向边。
接下来k行每行一个整数表示标记点编号。

O u t p u t Output Output

输出一个整数,表示最短距离,若没有方案可行输出-1。

S a m p l e Sample Sample I n p u t Input Input
3 3 2 1 1
1 2 1
2 3 1
3 1 1
2
3
S a m p l e Sample Sample O u t p u t Output Output
3

H i n t Hint Hint

路径为1->2->3->1。
20%的数据n<=10。
50%的数据n<=1000。
另有20%的数据k=0。
100%的数据n<=50000,m<=100000,0<=k<=10,1<=z<=5000。

T r a i n Train Train o f of of T h o u g h t Thought Thought

看到数据k很小
就知道是暴力了
我们可以先k+1个SPFA来算这k个特殊点和起始点的最短路
然后暴力全排列k个点的顺序

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;

int bol[50250], A[20], B[50250], H[50250], F[20];
int n, m, t, tot, start, end, x, y, z, g;
ll dis[20][50250];
ll Ans;

struct wh_
{
    
    
	int w, h, k;
}wh[100250];

void hw(int x, int y, int z)
{
    
    wh[++tot] = (wh_){
    
    y, H[x], z}; H[x] = tot;}

void SPFA(int l, int x)
{
    
    
	dis[l][x] = 0;
	queue<int>hy;
	hy.push(x);
	bol[x] = 0;
	while(hy.size())
	{
    
    
		int h = hy.front(); hy.pop();
		for(int i = H[h]; i; i = wh[i].h)
			if(dis[l][h] + wh[i].k < dis[l][wh[i].w])
			{
    
    
				dis[l][wh[i].w] = dis[l][h] + wh[i].k;
				if(!bol[wh[i].w])
				{
    
    
					hy.push(wh[i].w);
					bol[wh[i].w] = 1;
				}
			}
		bol[h] = 0;
	}
}

void Dfs(int now, ll sum)
{
    
    
	if(sum > Ans)return;//剪枝
	if(now == t)
	{
    
    
		sum += dis[B[F[now]]][end];
		Ans = min(Ans, sum);
		return; 
	}
	for(int i = 1; i <= t; ++i)
		if(!bol[A[i]])
		{
    
    
			bol[A[i]] = 1;
			F[now + 1] = A[i];
			Dfs(now + 1, sum + dis[B[F[now]]][A[i]]);
			bol[A[i]] = 0;
		}
	return;
}

int main()
{
    
    
	memset(dis, 0x7f, sizeof(dis));
	scanf("%d%d%d%d%d", &n, &m, &t, &start, &end);
	for(int i = 1; i <= m; ++i)
	{
    
    
		scanf("%d%d%d", &x, &y, &z);
		hw(x, y, z);//ps:有向边
	}
	g = 0;
	SPFA(0, start);//起始点SPFA
	ll MAX = dis[19][50200];
	if(dis[0][end] != MAX)g = 1;//判断是否可以联通终点
	for(int i = 1; i <= t; ++i)
	{
    
    
		scanf("%d", &A[i]);
		B[A[i]] = i;
		SPFA(i, A[i]);
		if(dis[i][end] != MAX)g = 1;
	}
//	printf(" ");
//	for(int i = 1; i <= n; ++i)
//		printf(" %d", i);
//	printf("\n%d", start);
//	for(int i = 1; i <= n; ++i)
//		printf(" %d", dis[0][i]);
//	for(int i = 1; i <= t; ++i)
//	{
    
    
//		printf("\n%d", A[i]);
//		for(int j = 1; j <= n; ++j)
//			printf(" %d", dis[i][j]);
//	}
	if(!g)
	{
    
    
		printf("-1");
		return 0;
	}
	F[0] = start, Ans = 1e15;//Ans要弄大一点,否则90分
	Dfs(0, 0);//全排列
	printf("%lld", Ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/SSL_wujiajie/article/details/108549347