【BJOJ1097】旅游景点 atr (spfa+状压dp)

来源:互联网 发布:客户无忧软件 编辑:程序博客网 时间:2024/04/28 01:57

题目描述

FGD想从成都去上海旅游。由于FGD非常讨厌乘车的颠簸,他希望在满足他的要求的情况下,旅行的距离尽量短,这样他就有足够

的精力来欣赏风景。

整个城市交通网络包含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需要的。

输入

第1行包含3个整数N(2< =N< =20000), M(1<=M<=200000),K(0< =K<=20)

第2..M+1行,每行包含3个整数X,Y,Z,(1 ≤ x < y ≤N, 1 ≤z ≤ 1000).表示城市x到城市y距离为z,数据保证必然存在一条路径从城市

1到城市n, 经过所有城市。

第M+2行,一个整数g, 0 ≤ g ≤k·(k−1)/2。表示FGD停留的限制条件有g个。

第M+3..M+2+g行,包含2个整数ri和si ,2 ≤ ri ≤ k +1, 2 ≤ si ≤ k +1, ri不等于si。FGD访问城市si之前,必须访问ri。

输出

只包含一行,包含一个整数,表示最短的旅行距离

思路

分析发现k个点的先后到达顺序影响答案,其他点无论何时访问对于答案的贡献是固定的。因此可以先把研究范围缩小到这k个点

中。鉴于k的大小比较友善,可以考虑状压这k个点的状态(已访问/未访问)。

先spfa预处理出这k个点到每个点的最短距离,设f[i][j]表示状态i下最后到达第j个点的最小最短路程,不难得出转移式:

f[s][i]=min(f[s'][j]+dis[i][j])

对于访问某些点之前必须先访问其他点的限制条件,不妨同样通过状压的方式存下访问点i之前必须访问的点,在在转移的时候跳过非法状态即可。

#include <queue>#include <cstdio>#include <iostream> using namespace std; const int MAXN=20010;const int MAXM=200010;const int MAXK=22;const int INF=1e9; int n,m,k,g;bool inq[MAXN]; int head[MAXN],tot,lim[MAXK],f[1<<20][MAXK],dis[MAXK][MAXN],ans=INF;struct edge{int to,nxt,c;}e[MAXM<<1]; int getint(){    int v=0; char ch;    while(!isdigit(ch=getchar())); v=ch-48;    while(isdigit(ch=getchar())) v=v*10+ch-48; return v;} void insert(int u,int v,int cost){    e[++tot].to=v; e[tot].nxt=head[u]; head[u]=tot; e[tot].c=cost;    e[++tot].to=u; e[tot].nxt=head[v]; head[v]=tot; e[tot].c=cost;} void spfa(int S){    for(int i=1;i<=n;i++) dis[S][i]=INF; dis[S][S]=0;    queue<int> Q; Q.push(S); inq[S]=true;    while(!Q.empty()){        int now=Q.front(); Q.pop();        for(int i=head[now];i;i=e[i].nxt){            if(dis[S][e[i].to]>dis[S][now]+e[i].c){                dis[S][e[i].to]=dis[S][now]+e[i].c;                if(!inq[e[i].to]){                    Q.push(e[i].to);                    inq[e[i].to]=true;                }            }        }        inq[now]=false;    }} int main(){    n=getint(); m=getint(); k=getint();    for(int i=1;i<=m;i++){        int a=getint(),b=getint(),c=getint();        insert(a,b,c);    }    g=getint();    for(int i=1;i<=g;i++){        int a=getint(),b=getint();        lim[b]|=(1<<(a-2));    }    for(int i=1;i<=k+1;i++) spfa(i);    for(int i=0;i<(1<<k);i++)        for(int j=1;j<=k+1;j++)            f[i][j]=INF;    for(int i=1;i<=k+1;i++)        if(!lim[i])            f[1<<(i-2)][i]=dis[1][i];    for(int i=0;i<(1<<k);i++){        for(int j=1;j<=k+1;j++){            if(f[i][j]==INF) continue;            for(int l=1;l<=k+1;l++){                if((lim[l]&i)!=lim[l]) continue;                f[i|(1<<(l-2))][l]=min(f[i|(1<<(l-2))][l],f[i][j]+dis[l][j]);            }        }    }    for(int i=1;i<=k+1;i++)        ans=min(ans,f[(1<<k)-1][i]+dis[i][n]);    printf("%d\n",ans);    return 0;}


0 0