PAT甲级1087 All Roads Lead to Rome (30分) 深度搜索 或者 dijkstra 后续再考虑使用队列优化

1087 All Roads Lead to Rome (30分)

Indeed there are many different tourist routes from our city to Rome. You are supposed to find your clients the route with the least cost while gaining the most happiness.
Input Specification:

Each input file contains one test case. For each case, the first line contains 2 positive integers N (2≤N≤200), the number of cities, and K, the total number of routes between pairs of cities; followed by the name of the starting city. The next N−1 lines each gives the name of a city and an integer that represents the happiness one can gain from that city, except the starting city. Then K lines follow, each describes a route between two cities in the format City1 City2 Cost. Here the name of a city is a string of 3 capital English letters, and the destination is always ROM which represents Rome.
Output Specification:

For each test case, we are supposed to find the route with the least cost. If such a route is not unique, the one with the maximum happiness will be recommanded. If such a route is still not unique, then we output the one with the maximum average happiness – it is guaranteed by the judge that such a solution exists and is unique.

Hence in the first line of output, you must print 4 numbers: the number of different routes with the least cost, the cost, the happiness, and the average happiness (take the integer part only) of the recommanded route. Then in the next line, you are supposed to print the route in the format City1->City2->…->ROM.
Sample Input:

6 7 HZH
ROM 100
PKN 40
GDN 55
PRS 95
BLN 80
ROM GDN 1
BLN ROM 1
HZH PKN 1
PRS ROM 2
BLN HZH 2
PKN GDN 1
HZH PRS 1

Sample Output:

3 3 195 97
HZH->PRS->ROM

这个题理解起来倒是不难,还是求最短路径的问题,唯一就是条件限制复杂点

先用深搜解决,因为n也不大 最多才200, 再加上路径截断(就是已经一个路径长度了,其他搜索深度超过路径长度的直接return 节省时间),深搜应该是不会超时的,

深搜的话主要就是

  • 记录哪些边访问过了,我们使用一个sign【300】的数目,默认都是0,经过哪些点了,赋值为1,return 回来了,就再赋值为0

在这里插入图片描述

  • 然后就是关于路径的记录,我们每一次条用find()深搜函数就深入了一次,也就是到达了另一个城市,那我们就肯定把这个城市记录下来,这样到达ROM城市了就形成了一个路径了,但是有个比较麻烦的就是,我们return的时候还需要把这个城市再删除,所以使用list来存储比较方便
    在这里插入图片描述
  • 哦,对了,还有就是我们保存路径,我使用了vector<vector> save;//用于保存搜索出的所有路径,每一个路径都是从list读取出来,保存到一个vector里面,再把这个vecrot添加到save里面

然后就是没啥了难的了,都是小细节需要注意
代码如下

#include <iostream>
#include <string>
using namespace std;
#include <unordered_map>
#include <vector>
#include <set>
#include <algorithm>
#include <list>

int N,K;
int start=0;//表示起点
int endd;//表示终点
int edge[300][300];//用来记录边的路径
unordered_map<string,int> str2int;//用来把城市名映射成城市编号
unordered_map<int,string> int2str;//用来把城市编号映射成城市名
int sign[300];//用于在深搜时候记录哪些城市访问过了

int minDis=99999;//用于记录最短的路径

unordered_map<int,int> happiness;//记录每个城市编号对应的开心值
list<int> listt;//用于记录搜索时候的路径
vector<vector<int>> save;//用于保存搜索出的所有路径

int number=0;//路径的数目

//pos是当前达到的城市,cost是到达该城市的路径
void def(int edge[300][300],int pos,int cost)
{
    //pos是记录到了那个城市,cost是到这个城市的距离
    if(pos==endd)
    {
        if(cost>minDis)
            return;
        else if(cost==minDis)
        {
            minDis=cost;
            number++;

            vector<int> getCurrent;
            getCurrent.push_back(0);
            for(auto it=listt.begin(); it!=listt.end(); it++)
            {
                getCurrent.push_back(*it);
            }

            save.push_back(getCurrent);
            return ;
        }
        else if(cost<minDis)
        {
            minDis=cost;
            number=1;


            vector<int> getCurrent;
            getCurrent.push_back(0);
            for(auto it=listt.begin(); it!=listt.end(); it++)
            {
                getCurrent.push_back(*it);
            }
            save.clear();
            save.push_back(getCurrent);
            return;
        }
    }
    else if(cost>=minDis)
        return;
    else
    {
        for(int i=1; i<N; i++)
        {
            if(i==pos)
                continue;
            if(edge[pos][i]!=-1&&sign[i]==0)
            {
                sign[i]=1;
                listt.push_back(i);
                def(edge,i,cost+edge[pos][i]);
                listt.pop_back();
                sign[i]=0;
            }
        }
    }
}

int main()
{
    sign[0]=1;//这个是我们默认的起始城市就是 编号0的城市,直接设置已经访问了
    cin>>N>>K;
    //N是城市数据,K是边的数目



    string sourch;  //起点城市的名字
    cin>>sourch;

    str2int[sourch]=0;//起点城市编号为0
    int2str[0]=sourch;

    int num=1;//还剩下N-1个城市,用num 来记录每个城市的标号
    for(int i=0; i<N-1; i++)
    {
        string str;

        int get;
        cin>>str>>get;
        if(str=="ROM")
            endd=num;
        str2int[str]=num;
        int2str[num]=str;

        happiness[num]=get;
        num++;
    }

    fill(edge[0], edge[0] + 300 * 300, -1);//对所有的变赋值为-1, 表示两两城市不通
    for(int i=0; i<K; i++)
    {
        string a,b;
        cin>>a>>b;
        int p;
        cin>>p;
        edge[str2int[a]][str2int[b]]=p;//记住是路径是双向的
        edge[str2int[b]][str2int[a]]=p;
    }


    def(edge,0,0);

    //把save里面保存的每个路径都比较一下
    //因为要求 最大的平均快乐,所以就是最小的城市数目
    int maxHappy=0,minCityNumber=0;
    for(auto it=save[0].begin(); it!=save[0].end(); it++)
    {
        //*it就是保存的城市的编号
        maxHappy+=happiness[*it];
        minCityNumber+=1;
    }
    vector<int> targetPos=save[0];//最初认为save保存的第一条路径就是所求的路径
    for(auto it=save.begin()+1; it!=save.end(); it++)
    {
        int cachMaxHappy=0,cachMinCityNumber=0;//遍历的路径的最大快乐,和最小城市数目
        for(auto itt=(*it).begin(); itt!=(*it).end(); itt++)
        {

            cachMaxHappy+=happiness[*itt];
            cachMinCityNumber+=1;
        }
        if(maxHappy<cachMaxHappy)
        {
            targetPos=*it;
            maxHappy=cachMaxHappy;
            minCityNumber=cachMinCityNumber;
        }
        else if(maxHappy==cachMaxHappy)
        {
            if(cachMinCityNumber<minCityNumber)
            {
                targetPos=*it;
                maxHappy=cachMaxHappy;
                minCityNumber=cachMinCityNumber;
            }
        }
    }
    cout<<number<<' '<<minDis<<' '<<maxHappy<<' '<<(int)(float)maxHappy/(minCityNumber-1)<<endl;

    for(auto it=targetPos.begin(); it!=targetPos.end(); it++)
    {
        int get=*it;
        if(it==targetPos.begin())
        {
            cout<<int2str[get];
        }
        else
        {
            cout<<"->"<<int2str[get];
        }
    }


    return 0;
}

下面尝试使用dijkstra 解决,使用dijkstra求最短路径并不难,这个题还是麻烦在路径的记录上
关于最短路径:Dijkstra算法去这个链接看,有图更好理解

首先dijkstra的应用范围,单源最短路径,权值不可为负,有向无向图均可
具体步骤如下:

  1. 我们有了一个起点,这个起点(a)到其他所有点的距离肯定有一个最小的(或者并列最小的,最小路径的点记为b),那么这也可以说 a 到b的最短路径就确定了,因为你从其他点比如c点绕路到达b,肯定比直接从a到b的距离长,所以不可能有其他路径小于这个路径长度了。
  2. 那么现在我们知道了a 到b的最短路径了,那么我们便可以使用b来优化a到其他点的路径了(也就是松弛操作)

因为这个题感觉限制条件蛮多的,城市点又不是很多,就不考虑dijkstra的优化方法(边结点和优先队列)直接使用二维数组表示边,dis数组表示到其他城市的距离
但因为这个题有很多限制什么最大快乐和最大平均快乐,路径的条数,所以我们还需要记录下所有的最短路径(路径记录下来了,其他的什么最大快乐啥的都不是大问题了),这里使用vector pre[300]来记录路径,比如我们前面说的确定了a到b的最短路径了,那么pre[b].push_back(a),这样我们可以直接使用深搜来找到所有的路径了。

另外应该在读取边的时候,就把和起点城市直接相连的城市 pre值赋值 为起点城市,因为如果人家最初就是最短的路径,后面更新pre的时候可能并不更新

代码:

#include <iostream>
#include <string>
using namespace std;
#include <unordered_map>
#include <vector>
#include <set>
#include <algorithm>
#include <list>
int number=0;//记录最短路径数目,这个在最后深搜过程用的到
int endIndex=-1;//记录ROM城市的编号,因为在深搜函数里面要用到,所以全局变量
vector<vector<int>> save;//保存路径
vector<int> pre[300];//保存最短路径下,每个城市的前面的那个城市编号
list<int> way;//用于深搜路径的绘制


void dfs(int cityIndex){
    if(cityIndex==0){
        vector<int> cache;//使用vector 保存way这个list的路径
        //cache.push_back(0);
        for(auto it=way.begin();it!=way.end();it++){
            cache.push_back(*it);
        }
        cache.push_back(endIndex);
        save.push_back(cache);
        number++;
    }

    for(int i=0;i<pre[cityIndex].size();i++){
        way.push_front(pre[cityIndex][i]);
        dfs(pre[cityIndex][i]);
        way.pop_front();

    }

}
int main()
{

    int N,K;



    cin>>N>>K;
    //N是城市数据,K是边的数目


    int start=0;//其实城市编号为0

    int edge[300][300];
    fill(edge[0],edge[0]+300*300,9999999);

    unordered_map<string,int> str2int;
    unordered_map<int,string> int2str;
    int sign[300];//表示那个城市最短路径找到了
    fill(sign,sign+300,0);
    sign[0]=1;
//从0 这个点开始,然后到达endIndex这个点,
//中间的距离最短,并且把路径保存下来
    int dis[300];//到达其他城市的路径
    fill(dis,dis+300,99999999);



    unordered_map<int,int> happ;//保存快乐值



    string sourch;//其实城市的名字
    cin>>sourch;

    str2int[sourch]=0;
    int2str[0]=sourch;

    int num=1;
    for(int i=0; i<N-1; i++)
    {
        string str;

        int get;
        cin>>str>>get;
        if(str=="ROM")
            endIndex=num;
        str2int[str]=num;
        int2str[num]=str;

        happ[num]=get;
        num++;
    }


    for(int i=0; i<K; i++)
    {
        string a,b;
        cin>>a>>b;
        int p;
        cin>>p;
        edge[str2int[a]][str2int[b]]=p;
        edge[str2int[b]][str2int[a]]=p;
        if(a==sourch)
        {
            dis[str2int[b]]=p;

            //这个很重要,因为我们再后面dijkstra查找最短路径的时候
            //只有遇到可以缩短某个城市距离的时候才更新pre值的
            //如果一个城市路径本身就是最短的pre值就不会更更新,就是下面的默认值
            pre[str2int[b]].push_back(0);
        }
        if(b==sourch)
        {
            dis[str2int[a]]=p;
            pre[str2int[a]].push_back(0);
        }

    }



    //反正N次肯定是足够选出存在的最短路径了
    for(int q=0; q<N; q++)
    {
        //需要用到的变量 dis edge pre sign

        //先要确定dis中哪些城市最近
        int minDis=-1;//保存dis中最短的距离
        list<int> shortCityIndex;
        for(int cityIndex=1; cityIndex<N; cityIndex++)
        {
            if(sign[cityIndex]==0)
            {
                if(minDis==-1)
                {
                    minDis=dis[cityIndex];
                    shortCityIndex.push_back(cityIndex);
                }
                else if(minDis==dis[cityIndex])
                {
                    shortCityIndex.push_back(cityIndex);
                }
                else if(minDis>dis[cityIndex])
                {
                    minDis=dis[cityIndex];
                    shortCityIndex.clear();
                    shortCityIndex.push_back(cityIndex);
                }
            }
        }
        if(shortCityIndex.size()==0)
            break;
        //dis中最近的城市都保存在了shortCityIndex里面了
        //对最近的城市进行处理,sign 赋值为1,只有对dis进行遍历后才能知道谁最短
        for(auto it=shortCityIndex.begin(); it!=shortCityIndex.end(); it++)
        {
            sign[*it]=1;

        }
        //不是以起点找到的最短城市的pre ,需要在放缩时候进行操作赋值了
        for(auto it=shortCityIndex.begin(); it!=shortCityIndex.end(); it++)
        {
            for(int i=1; i<N; i++)
            {
                //i是准备优化的城市编号
                if(sign[i]==0)
                {
                    if(dis[i]>dis[*it]+edge[*it][i])
                    {
                        dis[i]=dis[*it]+edge[*it][i];
                        pre[i].clear();
                        pre[i].push_back(*it);
                    }
                    else if(dis[i]==dis[*it]+edge[*it][i])
                    {
                        pre[i].push_back(*it);
                    }
                }
            }
        }
    }



    dfs(endIndex);
    int minDis=dis[endIndex];
     //把save里面保存的每个路径都比较一下
    int maxHappy=0,minCityNumber=0;
    for(auto it=save[0].begin(); it!=save[0].end(); it++)
    {
        //*it就是保存的城市的编号
        maxHappy+=happ[*it];
        minCityNumber+=1;
    }
    vector<int> targetPos=save[0];
    for(auto it=save.begin()+1; it!=save.end(); it++)
    {
        int cachMaxHappy=0,cachMinCityNumber=0;
        for(auto itt=(*it).begin(); itt!=(*it).end(); itt++)
        {

            cachMaxHappy+=happ[*itt];
            cachMinCityNumber+=1;
        }
        if(maxHappy<cachMaxHappy)
        {
            targetPos=*it;
            maxHappy=cachMaxHappy;
            minCityNumber=cachMinCityNumber;
        }
        else if(maxHappy==cachMaxHappy)
        {
            if(cachMinCityNumber<minCityNumber)
            {
                targetPos=*it;
                maxHappy=cachMaxHappy;
                minCityNumber=cachMinCityNumber;
            }
        }
    }
    cout<<number<<' '<<minDis<<' '<<maxHappy<<' '<<(int)(float)maxHappy/(minCityNumber-1)<<endl;

    for(auto it=targetPos.begin(); it!=targetPos.end(); it++)
    {
        int get=*it;
        if(it==targetPos.begin())
        {
            cout<<int2str[get];
        }
        else
        {
            cout<<"->"<<int2str[get];
        }
    }



    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值