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