返回顶部

旅游景点 Tourist Attractions

[POI2007] ATR-Tourist Attractions

题目背景

FGD想从成都去上海旅游。在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣的事情。经过这些城市的顺序不是完全随意的,比如说FGD

不希望在刚吃过一顿大餐之后立刻去下一个城市登山,而是希望去另外什么地方喝下午茶。幸运的是,FGD的旅程不是既定的,他可以在某些旅行方案之间进行选择。由

于FGD非常讨厌乘车的颠簸,他希望在满足他的要求的情况下,旅行的距离尽量短,这样他就有足够的精力来欣赏风景或者是泡MM了_. 整个城市交通网络包含N个城

市以及城市与城市之间的双向道路M条。城市自1至N依次编号,道路亦然。没有从某个城市直接到它自己的道路,两个城市之间最多只有一条道路直接相连,但可以有

多条连接两个城市的路径。任意两条道路如果相遇,则相遇点也必然是这N个城市之一,在中途,由于修建了立交桥和下穿隧道,道路是不会相交的。每条道路都有一个

固定长度。在中途,FGD想要经过K(K<=N-2)个城市。成都编号为1,上海编号为N,而FGD想要经过的N个城市编号依次为2,3,…,K+1. 举例来说,假设交通网络如下图。

FGD想要经过城市2,3,4,5,并且在2停留的时候在3之前,而在4,5停留的时候在3之后。那么最短的旅行方案是1-2-4-3-4-5-8,总长度为19。注意FGD为了从城市2到城市4

可以路过城市3,但不在城市3停留。这样就不违反FGD的要求了。并且由于FGD想要走最短的路径,因此这个方案正是FGD需要的。

题目描述

给出一张有 \(n\) 个点 \(m\) 条边的无向图,每条边有边权。

你需要找一条从 \(1\)\(n\) 的最短路径,并且这条路径在满足给出的 \(g\) 个限制的情况下可以在所有编号从 \(2\)\(k+1\) 的点上停留过。

每个限制条件形如 \(r_i, s_i\),表示停留在 \(s_i\) 之前必须先在 \(r_i\) 停留过。

注意,这里的停留不是指经过

输入格式

第一行三个整数 \(n,m,k\)

之后 \(m\) 行,每行三个整数 \(p_i, q_i, l_i\),表示在 \(p_i\)\(q_i\) 之间有一条权为 \(l_i\) 的边。

之后一行一个整数 \(g\)

之后 \(g\) 行,每行两个整数 \(r_i, s_i\),表示一个限制条件。

输出格式

输出一行一个整数,表示最短路径的长度。

样例 #1

样例输入 #1

8 15 4
1 2 3
1 3 4
1 4 4
1 6 2
1 7 3
2 3 6
2 4 2
2 5 2
3 4 3
3 6 3
3 8 6
4 5 2
4 8 6
5 7 4
5 8 6
3
2 3
3 4
3 5

样例输出 #1

19

提示

对于 \(100\%\) 的数据, 满足:

  • \(2\le n\le2\times10^4\)
  • \(1\le m\le2\times10^5\)
  • \(0\le k\le\min(20, n-2)\)
  • \(1\le p_i<q_i\le n\)
  • \(1\le l_i\le 10^3\)
  • \(r_i, s_i \in [2,k+1], r_i\not=s_i\)
  • 保证不存在重边且一定有解。

目前LUOGU状态
image

思路,状压DP+dij

  • 注意,我们初始化的时候,不能过大(如LONG_LONG_MAX),过大求最短路时会变成负数
    停留\(\neq\)经过
    \(f[i,j]\)表示当前二进制状态i以及在哪个点停留
    停留的为2~k+1
    如果下标从0开始
    转移为\(f[i|(1<<to)][j+2]=min(f[i|(1<<to)][j+2],f[i][j+2]+dis[j+2][to+2])\)
    第一层为状态,第三层停留的点,第二层是从点j-->第三层停留的点
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int N = 20000+5;
ll n,m,k,q;
ll dis[25][N],vis[N],head[N],cnt;
ll mp[N];
struct Edge
{
	int from,to,next;ll w;
}edge[N*10*2];
void add(int u,int v,ll w)
{
	edge[++cnt].from=u;
	edge[cnt].to=v;
	edge[cnt].next=head[u];
	edge[cnt].w=w;
	head[u]=cnt;	
}
struct node
{
	int from;ll w;
	bool operator < (const node &A)const
	{
		return w<A.w;
	}
};
void dij(int cen,int st)
{
//	memset(dis,0x7f,sizeof(dis)); 
	for(int i=1;i<=n;++i) dis[cen][i]=INT_MAX;
	memset(vis,0,sizeof(vis));
	priority_queue<node>q;
	q.push({st,0});
	dis[cen][st]=0;
	while(!q.empty())
	{
		int u=q.top().from;q.pop();
		if(vis[u])continue;
		vis[u]=1;
		for(int i=head[u];i;i=edge[i].next)
		{
			int to=edge[i].to;
			if(dis[cen][to]>dis[cen][u]+edge[i].w)
			{
				dis[cen][to]=dis[cen][u]+edge[i].w;
//				cout<<dis[cen][to]<<endl;
				q.push({to,-dis[cen][to]});
			}
		}
	}
}
void test(int x)
{
	bitset<10>a;
	a=x;
	cout<<"TEST:"<<a<<endl;
}
ll f[(1<<21)+5][25];
main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m>>k;
	int x,y,z;
	for(int i=1;i<=m;i++)
	{
		cin>>x>>y>>z;
		add(x,y,z);add(y,x,z);
	}	
	cin>>q;
	ll F,l;
	for(int i=1;i<=q;i++)
	{
		cin>>F>>l;
		mp[l]|=(1<<(F-2));//f must front l
	}	
	if(k==0)
	{
		dij(1,1);
		cout<<dis[1][n]<<endl;
		return 0;
	}
	for(int i=1;i<=k+1;i++)
	{
		dij(i,i);
//		cout<<dis[i][n]<<endl;
	}
	for(int i=0;i<(1<<k);i++)
		for(int j=1;j<=k+1;j++)
			f[i][j]=INT_MAX;
	
	f[0][1]=0;
	for(ll i=2;i<=k+1;i++)
	{
		if(!mp[i])f[1<<(i-2)][i]=dis[1][i];
//		cout<<dis[1][i]<<endl;
	}
	for(ll i=1;i<(1<<k);i++)
	{
		for(int j=0;j<k;j++)
		{
			if(i&(1<<j))//表示i情况包含j
				for(int e=0;e<k;e++)
				{
				 	if(!(i&(1<<e))&&(i|mp[e+2])==i)//表示i情况不包含e 并且i情况符合特殊限制
						f[i|(1<<e)][e+2]=min(f[i|(1<<e)][e+2],f[i][j+2]+dis[j+2][e+2]);
//					cout<<f[i|(1<<e)][e+2]<<endl;
				}	
	
		}
	}
	ll ans=INT_MAX;
    for(int i=2;i<=k+1;++i)
     	ans=min(ans,f[(1<<k)-1][i]+dis[i][n]);           
    cout<<ans;	
	return 0;
}

posted @ 2024-04-03 11:58  wlesq  阅读(7)  评论(0编辑  收藏  举报