找位置 冲刺 noip

找位置
代码测试区
题目:
Farmer John 想找一个最好的位置来建新农场,这样他每天可以少走些路。
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 的路。
输出格式:
如果他把农场建在最优的位置,FJ 每天需要行走的最短路线长度。
输入输出样例:
样例输入1:
5 6 3
1
2
3
1 2 1
1 5 2
3 2 3
3 4 5
4 2 7
4 5 10
样例输出1:
12
样例解释:
FJ 在5 号城镇建立农场。他每天的行走路线为5-1-2-3-2-1-5,路线长度为
12

题解:
知识点: 最短路、深搜(或者dp)
讲解:
对于这一题我们可以知道如果对于每一个城市都爆搜一遍的话时间肯定炸 (废话 ),为什么炸,分析一下我们发现我们会搜索很多没用的点所以我们基于贪心的思想对于每一个城市跑一边最短路,然后我们又发现时间还是gg,继续看,我们发现我们只需要计算对于每一个超市到每一个城市的距离,所以我们换个角度,对于每个超市都跑一边最短路,然后因为超市最多5个,所以是可行的,然后现在要解决的就是从哪个城市出发,到达超市的顺序是怎样的,对于这个问题也没有什么算法可以解决,所以暴力出奇迹,我们暴力枚举每一个城市,对于每一个城市我们深搜求的所有超市排列后的最小值,因为超市数很小所以暴力完全是ok的。
代码:

#include<iostream>
#include<cstdio>
#include<cstring> 
#include<queue>
using namespace std;
int di[10][10100],su[10],head[100100],Next[100100],ver[100100],edge[100100],size=0,vist[10100],check[10],k,n,v[10100];
priority_queue <pair <int,int> >q;
void add(int x,int y,int u)//边表尝龟操作 
{
    size++;
    Next[size]=head[x];
    head[x]=size;
    ver[size]=y;
    edge[size]=u;
    return ;
}
void dijkstra(int t)//单源最短路 
{
    for(int i=1;i<=n;i++)
        di[t][i]=1000000000;
    memset(vist,0,sizeof(vist));
    di[t][su[t]]=0;
    q.push(make_pair(0,su[t]));
    while(q.size())
    {
        int x=q.top().second;
        q.pop();
        if(vist[x])
            continue;
        vist[x]=1;
        for(int i=head[x];i;i=Next[i])
        {
            int y=ver[i],z=edge[i];
            if(di[t][y]>di[t][x]+z)
            {
                di[t][y]=di[t][x]+z;
                q.push(make_pair(-di[t][y],y));
            }
        }
    }
}
int work(int t,int r,int p)//我们递归排列超市顺序求最小,t表示我们目前已经排列到第t个超市,已经经过r个超市,最开始是从p号城市出发的 
{
    if(r==k)//我们所有城市都排列完了 
        return di[t][p];//返回最后一个到达的超市到我们出发城市的距离 
    int ans=1000000000;//赋一个极大的初值 
    for(int i=1;i<=k;i++)
    {
        if(check[i]==1)
            continue;
        check[i]=1;//已经排列过了标记 
        ans=min(ans,work(i,r+1,p)+di[t][su[i]]);
        check[i]=0;//恢复标记 
    }
    return ans;
}
int main()
{
    int m,Min=1000000000;//初始值一定要赋好 
    scanf("%d %d %d",&n,&m,&k);
    for(int i=1;i<=k;i++)
    {
        scanf("%d",&su[i]);
        v[su[i]]=1;//标记是超市 
    }
    for(int i=1;i<=m;i++)
    {
        int x,y,u;
        scanf("%d %d %d",&x,&y,&u);
        add(x,y,u);
        add(y,x,u);
    }
    for(int i=1;i<=k;i++)//对于每一个超市求一遍最短路 
        dijkstra(i);
    for(int i=1;i<=n;i++)
    {
        if(v[i])//不是超市 
            continue;
        for(int j=1;j<=k;j++)
        {
            check[j]=1;
            Min=min(Min,work(j,1,i)+di[j][i]);//work函数求的是对应条件下走剩下的路要的值 
            check[j]=0;
        }
    }
    printf("%d",Min);
    return 0;
}

希望大家可以AC,马上要noip了,冲刺noip,问鼎省队。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值