旅遊景點 Tourist Attractions

wlesq發表於2024-04-03

[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;
}

相關文章