[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狀態
思路,狀壓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;
}