题目传送门
题目描述:
FJ决定搬家,重新建设农场,以便最小化他每天的行程。
FJ搬往的区域有N(1 <= N <= 10,000)个城镇,共有M (1 <= M <= 50,000)条双向道路连接某些城镇,所有城镇都能找到互通路线。
有K (1 <= K <= 5)个城镇建有市场,FJ每天离开新农场后,都要光顾这K个城镇,并返回农场。FJ希望建设农场的城镇不包含市场。
请帮助FJ选择最佳城镇建设农场,使得他每天的行程最小。
solution:
这道题的n值比较大,但是k却只有5,我们可以从k入手。
题目中是需要让我们求一个点,让这个点以此经过k个特殊点,所经过的路径之和最小。
从题目中的k入手,我们先求的这k次单源最短路,求出每个点到这k个点的最短距离。
之后用
O
(
n
!
)
O(n!)
O(n!)的时间预处理出经过这k个点的顺序。
然后就简单,枚举每一个除了标记的城市,求出当前城市作为起点的最小路径值(这时就可以用预处理出的数组来固定经过k的顺序,取最小值),取出所有的最小值即可。
代码稍微有点长,不过思路非常好理解
Code
#include<bits/stdc++.h>
using namespace std;
typedef pair < int , int > pii;
#define mp make_pair
struct node{
int Next,y,v;
}e[1010100];
vector < int > Q[150];
int len=0,n,m,k,num=0,cnt=0,minn=100000000000;
int linkk[1011001];
int K[10101010];
int d[6][1010101];
int b[6];
bool f[6];
bool isk[100010];
bool vis[100010];
void insert(int x,int y,int v){
e[++len].Next=linkk[x];
linkk[x]=len;
e[len].y=y;
e[len].v=v;
}
void find_shortest(int id){
int x=K[id];
priority_queue < pii , vector < pii > , greater < pii > > q;
memset(vis,0,sizeof(vis));
q.push(mp(0,x));
d[id][x]=0;
while (!q.empty()){
int xx=q.top().second,vv=q.top().first;
q.pop();
if (vis[xx])continue;
vis[xx]=1;
for (int i=linkk[xx];i;i=e[i].Next)
if (d[id][xx]+e[i].v<d[id][e[i].y])
d[id][e[i].y]=d[id][xx]+e[i].v,q.push(mp(d[id][e[i].y],e[i].y));
}
}
void add(){
++cnt;
for (int i=1;i<=k;i++) Q[cnt].push_back(b[i]);
}
void dfs(int t){
if (t>k) {add();return;}
for (int i=1;i<=k;i++)
if (!f[i]){
b[++num]=i;
f[i]=1;
dfs(t+1);
b[num--]=0;
f[i]=0;
}
}
int ask(int st,int x){
int ans=0,last=st,now=Q[x][0];
ans+=d[now][st];
for (int i=1;i<Q[x].size();i++){
last=now,now=Q[x][i];
ans+=d[last][K[now]];
}
ans+=d[now][st];
return ans;
}//依次经过的权值和
int main(){
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
scanf("%d %d %d",&n,&m,&k);
for (int i=1;i<=k;i++) scanf("%d",&K[i]),isk[K[i]]=1;//标记k城市
for (int i=1,x,y,z;i<=m;i++){
scanf("%d %d %d",&x,&y,&z);
insert(x,y,z),insert(y,x,z);
}
memset(d,20,sizeof(d));
for (int i=1;i<=k;i++){
find_shortest(i);//K次最短路
}
dfs(1);//预处理
for (int i=1;i<=n;i++){
if (isk[i]) continue;
for (int j=1;j<=cnt;j++) minn=min(minn,ask(i,j));
}
printf("%d",minn);
return 0;
}