首页 > 技术文章 > 旅游景点 Tourist Attractions【状压dp】

hzoi-poozhai 2020-04-21 21:32 原文

旅游景点 Tourist Attractions【状压dp】

题目链接:https://www.luogu.com.cn/problem/P3451

大意:给出n个城市和城市间的道路,给定一个k,从1开始走,必须经过2-k+1的所有城市,问最小路程。

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

思路

每个城市只有到过和没到过两种情况,很自然的想到状压dp

用dijkstra求出1-k+1每个点的单源最短路 (我一开用的floyd貌似会炸,也可能我写的不对)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define pa pair<int, int>//用这个pair相当于省略了一个node结构体
using namespace std;
const int maxn = 20010, inf = 0x3f3f3f3f;
int n, m, K, len, ed, a[25], dis[25][25], d[maxn], head[maxn],dp[1<<20][25], u, v, w, q;
bool vis[maxn];
struct edge{//边结构体
    int to, nx, dis;
}e[400005];
void insert(int u, int v, int w){//插入边
    e[++len].to = v;
    e[len].dis = w;
    e[len].nx = head[u];
    head[u] = len;
}
void dijs(int x){//求最短路,标准dijkstra,不多说
    priority_queue<pa, vector<pa>, greater<pa> > q; //定义pair的小根堆
    for(int i=1; i<=n; i++) d[i] = inf, vis[i] = 0;//初始化d[],d[i]表示当前点到i的最短距离
    d[x] = 0;
    q.push(make_pair(0, x));
    while(!q.empty()){
        int now = q.top().second;
        q.pop();
        if(vis[now]) continue;
        vis[now] = 1;
        for(int i=head[now]; i; i=e[i].nx){
            if(d[now]+e[i].dis<d[e[i].to]){
				d[e[i].to]=d[now]+e[i].dis;
				q.push(make_pair(d[e[i].to],e[i].to));
			}
        }
    }
    for(int i=1; i<=K+1; i++) dis[x][i] = d[i];
    dis[x][0] = d[n];//一个小技巧,用0的位置存n,我们不必把dis开到n*n
}
void sol(){//状压dp
    for(int now=0; now<=ed; now++)//枚举当前状态
        for(int x=1; x<=K+1; x++)//枚举当前状态下最后到达的城市
            if(dp[now][x] != -1)//保证x是能到达的
                for(int i=2; i<=K+1; i++){//枚举中转城市
                    int to = (now | (1<<(i-2)));//到达中转城市后的状态
                    if((now & a[i]) == a[i])//满足限制条件
                        if(dp[to][i] > dp[now][x] + dis[x][i] || dp[to][i] == -1)
                            dp[to][i] = dp[now][x] + dis[x][i];
                }
}
int main(){
    scanf("%d%d%d", &n, &m, &K);
    ed = (1<<K) - 1;//最大状态
    for(int i=1; i<=m; i++){
        scanf("%d%d%d", &u, &v, &w);
        insert(u, v, w); insert(v, u, w);
    }
    for(int i=1; i<=K+1; i++) dijs(i);
    scanf("%d", &q);
    while(q--){
        scanf("%d%d", &u, &v);
        a[v] += 1<<(u-2);
    }
    memset(dp, -1, sizeof(dp));
    dp[0][1] = 0;
    sol();
    int ans = inf;
    for(int i=1; i<=K+1; i++)
        if(dp[ed][i] != -1) ans = min(ans, dp[ed][i]+dis[i][0]);
    printf("%d\n", ans);
    return 0;   
}

推荐阅读