旅游景点 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
", ans);
return 0;
}