ssoj2460拜访女神(状压dp)

题目描述

TRT出国后,想找一个好的位置住下来。而他所在的城市,恰好有N栋建筑(从1~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 ~ M+2行:描述这些道路 对于每一行 三个正整数描述这条道路 u,v,d

输出

包含两行。

第一行是他所住的建筑物的编号(如果有多种选择,请输出编号最小的那一个)。

第二行是所有女神的焦急程度的最大值的最小值。

样例输入

6 10 22 51 5 25 6 36 2 45 3 16 3 22 3 31 2 41 4 33 4 52 4 2

样例输出

3 5

提示

样例解释


输入解释: 有6栋,2和5号有女神。有10条路。


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


数据规模与约定


良心出题人保证


对于20%的数据N<=8,M<=15


对于另外20%的数据 K=1


对于60%的数据 K<=5


对于100%的数据


N<=10^4,M<=5*10^4,K<=12,N>K,1<=p[i],u,v<=N,d<=10^4


本题后80%的数据保证随机


【思路】记录每个女神到达其它所有女神的最短路,只要枚举非女神的点为起点,走到第一个女神点,再加上她到其它女神的最短路。ans取最小。

【题解】


【注意】位运算优先级小于关系运算符

【代码】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=10004;
struct data{
    int v,nxt,w;
}e[maxn*11];
int n,m,k,p[22],dst[22][maxn],ans,ansu,fst[maxn],cnt=0,f[22][22][maxn],d[22];
bool gdn[maxn],vst[maxn];
queue<int>q;
inline int get(){
    char c;while(!isdigit(c=getchar()));
    int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
    return v;
}
inline void add(int x,int y,int z){e[++cnt].v=y;e[cnt].w=z;e[cnt].nxt=fst[x];fst[x]=cnt;}
inline void spfa(int num,int s){
    memset(vst,0,sizeof(vst));
    dst[num][s]=0;vst[s]=1;q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=fst[x];i;i=e[i].nxt){
            int y=e[i].v;
            if(dst[num][y]>dst[num][x]+e[i].w){
                dst[num][y]=dst[num][x]+e[i].w;
                if(!vst[y])q.push(y),vst[y]=1;
            }
        }
        vst[x]=0;
    }
}
int main(){
    n=get(),m=get(),k=get();
    for(int i=1;i<=k;++i)p[i]=get(),gdn[p[i]]=1;
    for(int i=1;i<=m;++i){
        int x=get(),y=get(),z=get();
        add(x,y,z);add(y,x,z);
    }
    memset(dst,0x1f,sizeof(dst));
    for(int i=1;i<=k;++i)spfa(i,p[i]);
    memset(f,0x1f,sizeof(f));
    memset(d,0x1f,sizeof(d));
    ans=d[0];
    int fs=1<<k;
    for(int i=1;i<=k;++i){
        int ts=1<<(i-1);
        f[i][i][ts]=0;
        for(int s=ts;s<fs;++s)if(s&ts){
            for(int j=1;j<=k;++j)if(s&(1<<(j-1))){
                for(int t=1;t<=k;++t)if((s&(1<<(t-1)))==0){
                    f[i][t][s|(1<<(t-1))]=min(f[i][t][s|(1<<(t-1))],f[i][j][s]+dst[j][p[t]]);
                }
            }
        }
        for(int j=1;j<=k;++j)d[i]=min(d[i],f[i][j][fs-1]);
    }
    for(int i=1;i<=n;++i)if(!gdn[i]){
        for(int j=1;j<=k;++j)if(d[j]+dst[j][i]<ans)ans=d[j]+dst[j][i],ansu=i;
    }
    printf("%d %d\n",ansu,ans);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值