DTOJ2460 拜访女神

58 篇文章 0 订阅

题目

题目描述

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

输入格式

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

输出格式

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

样例

样例输入

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

样例输出

3 5

数据范围与提示

样例解释

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

数据规模与约定

对于 20 % 20\% 20%的数据 N ⩽ 8 , M ⩽ 15 N\leqslant 8,M\leqslant 15 N8,M15
对于另外 20 % 20\% 20%的数据 K = 1 K=1 K=1
对于 60 % 60\% 60%的数据 K ⩽ 5 K\leqslant 5 K5
对于 100 % 100\% 100%的数据 N ⩽ 1 0 4 , M ⩽ 5 × 1 0 4 , K ⩽ 12 , N > K , 1 ⩽ p i , u , v ⩽ N , d ⩽ 1 0 4 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 N104,M5×104,K12,N>K,1pi,u,vN,d104
本题后 80 % 80\% 80%的数据保证随机

题解

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

#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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值