转载大神的题解。身为初学者我学了两个晚上才看懂。加了点注释方便大家阅读代码
找位置
Problem
问题描述
FJ 想找一个最好的位置来建新农场,这样他每天可以少走些路。FJ 所在的区域,有 N个城镇(1 <= N <= 10,000),城镇之间,有 M(1 <= M <= 50,000)条双向路相连,所有城镇都可以借助一些路相互连接。
FJ 需要你的帮助来选择最合适建新农场的城镇。K(1 <= K <= 5)个城镇中有超市,FJ 每天都会去这些超市。他计划每天从新农场出发,访问包含超市的 K 个城镇,然后返回新农场。FJ 可以按照任意的顺序访问这些超市。FJ 只会在 N-K 个城镇中,选择一个城镇来建新农场。因为其他城镇的房价,比较低一些。如果他把农场建在最优的位置,而且尽可能明智的选择行走路线。
请帮 FJ 计算,他每天需要行走的路线长度。
输入
第 1 行:三个空格隔开的整数,N,M 和 K。
第 2..1+K 行:第 i+1 行包含一个整数,范围 1..N,表示包含第 i 个超市的城镇。每个超市在不同城镇。
第 2+K..1+K+M 行:每行包含三个空格隔开的整数,i,j(1 <= i,j <= N),和 L(1 <= L <=1000), 表示城镇 i 和城镇 j 之间存在一条长度为 L 的路。
输出
输出一行一个数,表示他把农场建在最优的位置,每天需要行走的最短路线长度。
输入输出样例
relocation.in
5 6 3
1
2
3
1 2 1
1 5 2
3 2 3
3 4 5
4 2 7
4 5 10
relocation.out
12
Solution
做K 遍单源最短路,把K 个超市到每个点的最短路算出来
再用K!的时间预处理dp[i][j],表示先去第i 个超市,最后去第j 个超市的最短
时间
最后枚举所有的非超市点,即新建位置,并枚举先去的超市和最后去的超市,统
计答案
#include
#include
#include
#include
#include
using namespace std;
const int INF=0x3f3f3f3f; // 0x3f3f3f3f 为极大数=1061109567,是10^9级别的
int store[10],edge[100010],nxt[100100],val[100100],ecnt,adj[100100],go[100100],dis[7][100100],que[4000000],len,linkedfront[7];
int n,m,k,ans=INF;
bool isstore[100100],vis[100100],islink[7];
inline int read() //快速读入,比cin和scanf都要快的多
{
bool ok=0;int res=0;char c;
while(((c=getchar())<'0'||c>'9')&&c!='-');
if(c=='-') ok=1; else res=c-48;
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+(c-48); //左移n为表示扩大2的n次方被 res*8+res*2=res*(8+2)=res*10
return ok == 1 ? ~res+1:res ;// ~表示按位取反 ~n+1把n变成负数
}
void addedge(int u,int v,int w)
{
nxt[++ecnt]=adj[u];adj[u]=ecnt;go[ecnt]=v;val[ecnt]=w;
}
void spfa(int st,int r)
{
que[len=1]=st;
memset(dis[r],INF,sizeof(dis[r])); dis[r][st]=0;
for(int i=1;i<=len;i++)
{
int u=que[i];vis[u]=0; //que记录 最短路径起点 当队列来使用
for(int e=adj[u],v;e;e=nxt[e]) //adj记录某城镇的边 go记录此边的终点 nex记录此边的下一边
{
if(dis[r][u]+val[e]ans) return;
if(linked>k)
{
ans=min(ans,now+dis[linkedfront[linked-1]][st]);
return;
}
for(int i=1;i<=k;i++)
{
if(!islink[i])
{
islink[i]=1;linkedfront[linked]=i;
dfs(linked+1,now+ (linked==1 ?dis[i][st] : dis[i][store[linkedfront[linked-1]]]),st);
islink[i]=0;
}
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
n=read();m=read();k=read(); //有n个城镇 m条双向边 k个商店
for(int i=1;i<=k;i++) store[i]=read(),isstore[store[i]]=1;
int x,y,w;
for(int i=1;i<=m;i++)
{
x=read();y=read();w=read();
addedge(x,y,w);
addedge(y,x,w);
}
for(int i=1;i<=k;i++)spfa(store[i],i); //遍历含超市的城镇
for(int i=1;i<=n;i++) if(!isstore[i]) dfs(1,0,i);
printf("%d\n",ans);
// fclose(stdin);
// fclose(stdout);
}