【图论·动态规划·习题】最短路

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/87916543

Problem

题目描述

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

输入格式

第一行5个整数n、m、k、s、t,表示点个数、边条数、标记点个数、起点编号、终点编号。

接下来m行每行3个整数x、y、z,表示有一条从x到y的长为z的有向边。

接下来k行每行一个整数表示标记点编号。

输出格式

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

题解

设经过的第i个标记点的编号为 n e d i ned_{i} ,则所经过的路径是 s n e d 1 n e d 2 . . . n e d k t s→ned_{1}→ned_{2} →...→ned_{k}→t .

我们所求的就是上面这一条路径的长度最小。

观察数据范围,我们发现k很小,那个我们可以对每一个 n e d i ned_{i} 跑一边最短路,再对起点跑一边最短路。

现在每两个 n e d ned 和起点之间的最短路径搞好了,需要考虑顺序了;我们可以用状压DP解决。

f [ i ] [ j ] f[i][j] 表示经过的点数状态为 i i ,以 j j 结尾的路径最小值。

此时有状态转移方程: f [ i ] [ j ] = m i n ( f [ i   a n d   n o t ( 1 < < j 1 ) ] [ p ] + l e n [ p ] [ n e d [ j ] ] ) f[i][j]=min(f[i\ and\ not(1<<j-1)][p]+len[p][ned[j]])

至于初始化,暴力搞一遍每一个 n e d ned 到起点的具体即可。

最后的结果为: a n s = m i n ( f [ ( 1 < < k ) 1 ] [ i ] + l e n [ i ] [ t ] ) , 1 i k ans=min(f[(1<<k)-1][i]+len[i][t]),1≤i≤k

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,m,ans=0;
int s[60000];
int in[60000];
queue<int>q;
vector<int>a[60000]; 
int main(void)
{
	freopen("triangle.in","r",stdin);
	freopen("triangle.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%d",s+i);
	scanf("%d",&m);
	for (int i=1,x,y,z;i<=m;++i)
	{
		scanf("%d %d %d",&x,&y,&z);
		if (z==1 || z==2) a[x].push_back(y),in[y]++;
		if (z==4 || z==5) a[y].push_back(x),in[x]++;
	}
	for (int i=1;i<=n;++i) 
	    if (!in[i]) q.push(i);
	while (q.size())
	{
		int now=q.front();
		q.pop();
		for (int i=0;i<a[now].size();++i)
		{
			int next=a[now][i];
			in[next]--;
			if (!in[next]) q.push(next);
			if (!s[now]) s[next]^=1;
		}
		if (!s[now]) ans++;
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Ronaldo7_ZYB/article/details/87916543