链接:https://www.nowcoder.com/acm/contest/109/E
来源:牛客网
时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给定一幅n个点m条边的图和S个一定要经过的点,问从0号点出发,经过这S个点再回到0号点的最短路径长度是多少。
输入描述:
第一行一个整数T(T <= 2)表示数据组数。
对于每组数据,第一行两个整数n,m表示点数和边数(1 <= n, m <= 100,000)。
接下来m行,每行三个整数x, y, z(0 < x, y < n, 0 <= z <= 1000)表示xy之间有一条长度为c的双向边;
接下来一个整数S。(S<=10)
接下来S行每行一个整数表示一定要经过的点。
数据保证有解。
输出描述:
T行,每行一个整数表示答案。
示例1
输入
1 4 6 0 1 1 1 2 1 2 3 1 3 0 1 0 2 5 1 3 5 3 1 2 3
输出
4
思路:
发现到 只有10,因此整张图有用的点只有不超过 个( 个点和 号点),因此我们先跑若干次最短路可以得到这些点两 两之间的最短路.
处理完以后我们可以全拍列枚举中间 个点的顺序来做
也可以用状压 来做 表示当前选的点的状态为s ,最后一步走到的位置是 j的情况下最短路径长度 然后我们可 以用 不 在s 内点 去更新dp[s][j]
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int INF=0x3f3f3f3f;
struct node{int to,w;};
typedef pair<int,int> P;
vector<node>edge[N];
int n,m,x,y,val,s;int vis[N],dis[12][N],S[11];
int ans[1<<(12)][12];
void spfa(int s,int pos){
memset(vis,0,sizeof(vis));
dis[pos][s]=0;
priority_queue<P,vector<P>,greater<P> >q;
q.push({0,s});
while(!q.empty()){
int u=q.top().second;q.pop();
if(vis[u]) continue;vis[u]=1;//打上标记,说此时这个边已经是最短了
for(int i=0;i<edge[u].size();i++){
int v=edge[u][i].to,w=edge[u][i].w;
if(dis[pos][v]>dis[pos][u]+w){
dis[pos][v]=dis[pos][u]+w;
q.push({dis[pos][v],v});
}
}
}
}
void init(){
for(int i=0;i<n;i++) edge[i].clear();
}
int main(){
int t;scanf("%d",&t);
while(t--){
init();
memset(dis,0x3f,sizeof(dis));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&val);
edge[x].push_back({y,val});
edge[y].push_back({x,val});
}
spfa(0,0);S[0]=0;
scanf("%d",&s);
for(int i=1;i<=s;i++){
scanf("%d",&x);
spfa(x,i);S[i]=x;
}
memset(ans,0x3f,sizeof(ans));ans[1][0]=0;
for(int i=1;i<=(1<<(s+1));i++)
for(int j=0;j<=s;j++){
if(ans[i][j]!=INF){
for(int k=0;k<=s;k++){
if((i&(1<<k))==0&&dis[j][S[k]]!=INF){
ans[i|(1<<k)][k]=min(ans[i|(1<<k)][k],ans[i][j]+dis[j][S[k]]);
}
}
}
}
int ans_=INF;
int all=(1<<(s+1))-1;
for(int i=0;i<=s;i++) ans_=min(ans_,ans[all][i]+dis[0][S[i]]);
printf("%d\n",ans_);
}
return 0;
}