BZOJ 1097: [POI2007]旅游景点atr 最短路 堆优Dijkstra 状压

Time Limit: 30 Sec Memory Limit: 357 MB
Submit: 2021 Solved: 515

Description

  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,总长度为1
9。注意FGD为了从城市2到城市4可以路过城市3,但不在城市3停留。这样就不违反FGD的要求了。并且由于FGD想要
走最短的路径,因此这个方案正是FGD需要的。

Input

  第一行包含3个整数N(2<=N<=20000),M(1<=M<=200000),K(0<=K<=20),意义如上所述。

Output

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

Sample Input

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

Sample Output

19

HINT

BZOJ 1097: [POI2007]旅游景点atr 最短路 堆优Dijkstra 状压_第1张图片
上面对应于题目中给出的例子。


好题2333,我们要先把前面的城市先全部走完,然后再走后面的城市,于是我们对每一个前面的城市跑一遍单源最短路,然后状压求一下走完前面所有点并且最后在某个点的最小花费,然后再用这个点到终点的值加上走完所有点最后停留在这个点的最小花费来更新答案即可,正好我的状压比较差,这道题很不错诶
另外通过这道题抄了一段网上大佬的博客,dijkstra堆优的从来没见过的清真写法,真是666


#include 
using namespace std;
struct node {
    int v, to;
    bool operator < ( const node &p ) const { return v > p.v; }
};
const int MAXM = 200000 + 10; 
const int MAXN = 200000 + 10;
priority_queue  h;
int head[MAXN], tail, d[MAXN], n, m, k, x, y, z, dis[100][100], bin[100], situ[MAXN], dp[ 1 << 21 ][25], inf, S1;
struct Line{ int to, nxt, flow; }line[ MAXN * 2 ];
void add_line( int from, int to, int flow ) {
    line[++tail].nxt = head[from];
    line[tail].to = to;
    line[tail].flow = flow;
    head[from] = tail;
} 
void add_a_point( int u ) {
    for( register int i = head[u]; i; i = line[i].nxt ) 
        if( d[line[i].to] == -1 ) h.push( ( node ) { line[i].flow + d[u], line[i].to } );
}
void dijkstra( int S ) {
    memset( d, -1, sizeof(d) );
    while( !h.empty() ) h.pop();
    d[S] = 0; add_a_point(S); int fa;
    while( !h.empty() ) {
        if( d[ h.top().to ] != -1 ) { h.pop(); continue; }
        fa = h.top().to; d[fa] = h.top().v;
        h.pop(); add_a_point(fa);
    }
}
int main( ) {
    scanf( "%d%d%d", &n, &m, &k );
    for( register int i = 1; i <= m; i++ ) { scanf( "%d%d%d", &x, &y, &z ); add_line( x, y, z ); add_line( y, x, z ); }
    for( register int i = 1; i <= k + 1; i++ ) {
        dijkstra( i ); dis[i][0] = d[n];
        for( register int j = 1; j <= k + 1; j++ ) dis[i][j] = d[j];
    } bin[0] = 1;
    for( register int i = 1; i <= k + 1; i++ ) bin[i] = bin[ i - 1 ] << 1;
    scanf( "%d", &x );
    for( register int i = 1; i <= x; i++ )
        scanf( "%d%d", &y, &z ), situ[z] += bin[ y - 2 ];
    memset( dp, 0x3f, sizeof( dp ) ); inf = dp[0][0]; dp[0][1] = 0;
    for( register int S = 0; S < bin[k]; S++ )
        for( register int i = 1; i <= k + 1; i++ ) 
            if( dp[S][i] != inf )
                for( register int j = 2; j <= k + 1; j++ ) {
                    S1 = S | bin[ j - 2 ];
                    if( ( S1 & ( situ[j] ) ) == situ[j] ) 
                        dp[S1][j] = min( dp[S1][j], dp[S][i] + dis[i][j] );
                }
    int ans = 2147483647;
    for( register int i = 1; i <= k + 1; i++ ) 
        ans = min( ans, dp[ bin[k] - 1 ][i] + dis[i][0] );
    printf( "%d\n", ans );
    return 0;
}

这里写图片描述

你可能感兴趣的:(最短路,状压DP)