题目链接:https://ac.nowcoder.com/acm/contest/109/E?&headNav=www
题意:
给定一幅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
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
思路:这个题 需要抓住s的范围,最大是10,所以,需要关注的只有那s个点,可以用spfa进行s点与其他点之间的最短路更新,这是比较有趣 的,进行s+1次spfa最短路更新。然后就是每个点的便利顺序问题,再次利用s的值较小,就可以用状压dp解决,这也是比较常用的状压方法,整个题,还是 比较有趣的,简单最短路问题与简单状压的dp的结合,但是 自己做,还真的够呛能想到,,,
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,s,b[15],dis[15][100005],dp[1<<12][15],f[15];
struct node
{
int to,d;
}a[100005];
queue<node>t;
vector<node>q[100005];
void spfa(int st)
{
node tmp;tmp.to=b[st];tmp.d=0;
t.push(tmp);dis[st][b[st]]=0;
while(t.empty()==0)
{
node now=t.front();t.pop();
for(int i=0;i<q[now.to].size();i++)
{
node v=q[now.to][i];
if(dis[st][v.to]>dis[st][now.to]+v.d)
{
dis[st][v.to]=dis[st][now.to]+v.d;
//cout<<st<<" "<<v.to<<" ! "<<dis[st][v.to]<<endl;
node tt;tt.to=v.to;tt.d=dis[st][v.to];
t.push(tt);
}
}
}
}
int main(void)
{
int T,x,y,z;
scanf("%d",&T);
while(T--)
{
memset(dp,127,sizeof(dp));
memset(dis,127,sizeof(dis));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
node tmp;
tmp.to=y;tmp.d=z;
q[x].push_back(tmp);
tmp.to=x;tmp.d=z;
q[y].push_back(tmp);
}
scanf("%d",&s);
for(int i=1;i<=s;i++)
{
scanf("%d",&b[i]);
spfa(i);
}
spfa(0);
for(int i=0;i<=s;i++)
dp[1<<i][i]=dis[0][b[i]];
for(int i=0;i<(1<<(s+1));i++)
{
for(int j=0;j<=s;j++)
{
if(dp[i][j]>100000000) continue;
for(int k=0;k<=s;k++)
dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+dis[j][b[k]]);
}
}
printf("%d\n",dp[(1<<(s+1))-1][0]);
for(int i=1;i<=n;i++)
q[i].clear();
}
return 0;
}
/*
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
*/