链接:https://ac.nowcoder.com/acm/problem/16122
来源:牛客网
题意:
给n个点和m条路径和R个必经点,求经过这R个必经点的最短路径长度
思路:
状压DP,套个模板先,下次再补(下次也不一定)
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
ll n,m,R,dis[209][209],r[20],a,b,c,dp[20][1<<15],ans=inf;
int main() {
cin>>n>>m>>R;
for(int i=1; i<=R; i++) cin>>r[i];//需要去玩耍的郊区编号
for(int i=1; i<=n; i++) {
for (int j=1; j<=n; j++) {
if (i!=j) dis[i][j]=inf;
}
}
for(int i=1; i<=m; i++) {
cin>>a>>b>>c;
dis[b][a]=dis[a][b]=min(dis[a][b],c); //表示a到b的最短路径
}
//Floyd求最短路
for(int k=1; k<=n; k++) {
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
//初始化dp数组
memset(dp,0x3f,sizeof(dp));
for(int i=1; i<=R; i++) dp[i][1<<(i - 1)] = 0;//从第i个地点开始时花费为0
for(int s=1; s<(1<<R)-1; s++) { //表示去过的地点二进制集合的十进制表示
for(int i=1; i<=R; i++) {
if(s & (1<<(i-1))) {//s的二进制的第i位为1,即已经去了第i个地点
//那么我们就可用当前的数据去更新还未去的地点的数据
for(int j=1; j<=R; j++) {
if((s & (1<<(j-1)))==0) {//s的二进制的第j位为0,即第j个地点没有去过
dp[j][s+(1<<(j-1))]=min(dp[j][s+(1<<(j-1))],dp[i][s]+dis[r[i]][r[j]]);
//dp[i][j]表示当前在i地点,已经访问过结点集合为j,所经过路径的最小花费
}
}
}
}
}
for (int i=1; i<=R; i++) {
ans = min(ans,dp[i][(1<<R)-1]);
}
cout<<ans<<endl;
}