DTOJ2460 拜访女神

题目

题目描述

TRT出国后,想找一个好的位置住下来。而他所在的城市,恰好有N栋建筑(从$1\sim n$编号),他会选择这些建筑的某一个居住
而建筑之间,有$M$条双向路相连。每条道路有一个起始点$u$,终止点$v$,以及走过这条道路所需的时间$d$
所有建筑都可以借助一些道路相互到达
TRT每天会从他的住房出发,按任意顺序拜访他的$K$个女神(他想怎么走就怎么走),不过由于TRT精力(???)有限,他的女神个数不会超过$12$个
可他的女神们都比较娇气,希望TRT尽快来看她们可她们却不会担心TRT的花心……
对于第$i$个女神,她住在第p[i]栋建筑物,她每等$x$分钟(从TRT离家的那一时刻开始计算),她的焦急程度(初始为$0$)就会增加$x$
TRT当然希望她们高兴越好,而且他也不会让某个女神特别伤心,所以他希望所有女神的焦急程度的最大值越小越好
且他也不希望与任何一个女神住在一起,要不然会被众人黑成傻逼的虽然他已经被我们黑成傻逼了
所以他向你求助,帮他找出他应该住的那栋建筑,以及此时所有女神焦急程度最大值

输入格式

第一行:三个正整数$N,M,K$
第二行: $K$个正整数 第$i$个正整数即是$p_i$
第$3\sim M+2$行:描述这些道路 对于每一行 三个正整数描述这条道路$u,v,d$

输出格式

一行包含两个数
第一个是他所住的建筑物的编号(如果有多种选择,请输出编号最小的那一个)
第二个是所有女神的焦急程度的最大值的最小值

样例

样例输入

1
2
3
4
5
6
7
8
9
10
11
12
6 10 2
2 5
1 5 2
5 6 3
6 2 4
5 3 1
6 3 2
2 3 3
1 2 4
1 4 3
3 4 5
2 4 2

样例输出

1
3 5

数据范围与提示

样例解释

TRT在$3$号建筑安家。他每天的行走路线为$3\rightarrow 5\rightarrow 3\rightarrow 2$
到达$5$号建筑的时刻是$1$,那位女神的焦急程度是$1$
到达$2$号建筑的时间是$5$,那位女神的焦急程度是$5$,最大是$5$
接下来他爱走哪走哪,反正他已经拜访 (∗∗) 了所有女神。 可以证明其它方法不比这更优

数据规模与约定

对于$20\%$的数据$N\leqslant 8,M\leqslant 15$
对于另外$20\%$的数据$K=1$
对于$60\%$的数据$K\leqslant 5$
对于$100\%$的数据$N\leqslant 10^4,M\leqslant 5\times 10^4,K\leqslant 12,N>K,1\leqslant p_i,u,v\leqslant N,d\leqslant 10^4$
本题后$80\%$的数据保证随机

题解

看看这个$k$,它这么小,难道不香吗?
显然是个状压DP
首先跑$k$遍SPFA,把图压缩成只包含女神的住处,边为两两的最短路的图
设$f_{i,j,s}$表示起点为$i$,当前城市为$j$,遍历状态为$s$的最短时间
转移就是直接枚举上一个拜访的女神的房子,最后加上到起点的最近城市的距离就可以了
附上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
struct ppap
{
int to,nxt,ver;
}e[100010];
queue<int> q;
int n,m,k,tot,h,ans,p[20],w[10010],head[10010],dis[20][10010],v[10010],f[20][20][10010],g[20];
void add(int u,int v,int w)
{
e[++tot].nxt=head[u],head[u]=tot,e[tot].to=v,e[tot].ver=w;
}
void spfa(int k,int x)
{
memset(v,0,sizeof(v)),dis[k][x]=0,v[x]=1,q.push(x);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt) if(dis[k][e[i].to]>dis[k][u]+e[i].ver){
dis[k][e[i].to]=dis[k][u]+e[i].ver;
if(!v[e[i].to]) q.push(e[i].to),v[e[i].to]=1;
}
v[u]=0;
}
}
int main()
{
freopen("godness.in","r",stdin);
freopen("godness.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++) scanf("%d",&p[i]),w[p[i]]=1;
for(int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
memset(dis,0x1f,sizeof(dis)),memset(f,0x1f,sizeof(f)),memset(g,0x1f,sizeof(g)),ans=g[0];
for(int i=1;i<=k;i++) spfa(i,p[i]);
for(int i=1;i<=k;i++){
f[i][i][1<<(i-1)]=0;
for(int s=(1<<(i-1));s<(1<<k);s++) if(s&(1<<(i-1))) for(int j=1;j<=k;j++) if(s&(1<<(j-1)))
for(int t=1;t<=k;t++) if(!(s&(1<<(t-1))))
f[i][t][s|(1<<(t-1))]=min(f[i][t][s|(1<<(t-1))],f[i][j][s]+dis[j][p[t]]);
for(int j=1;j<=k;j++) g[i]=min(g[i],f[i][j][(1<<k)-1]);
}
for(int i=1;i<=n;i++) if(!w[i]) for(int j=1;j<=k;j++) if(g[j]+dis[j][i]<ans) ans=g[j]+dis[j][i],h=i;
printf("%d %d",h,ans);
}