蓝桥杯 The Traveling Judges Problem 【枚举 & 最小生成树 & 搜索】

版权声明:记笔记~~~ https://blog.csdn.net/weixin_42765557/article/details/87967603

问题描述

  一组人要担任在一个特定城市举办的比赛的评委,他们需要找到最便宜的租车方式使得每个人都到达目标城市。他们观察发现,如果几个人在旅途的某一段坐同一辆租的车,就可以减少总费用。你的任务就是找出这些人应该采取的路线使得租车的总费用最小。
  我们假定:
  1. 租一辆车的费用与它行驶的距离成正比,没有燃油、保险、乘客人数多于一个等产生的额外费用。
  2. 所有车的费用与行驶距离的比例相同。
  3. 一辆车可以容纳任意数量的乘客。
  4. 任意一对城市之间最多只有一条道路直接相连,每条道路都是双向的且长度大于0。
  5. 每个人的起始城市到目标城市都至少有一种路线。
  6. 若多个人的路线中经过同一城市,则这些人从该城市到目标城市必乘同一辆车。
  7. 一个人可以乘一辆车到某个城市,再乘另一辆车离开该城市。

输入格式

  第一行包含三个整数nc, dc和nr,表示地图上的城市个数,目标城市的编号和地图上的道路条数。
  接下来nr行每行包含三个整数c1, c2和dist,表示一条长度为dist的双向道路(c1, c2)。
  接下来一行包含一个整数nj,表示人数。
  接下来一行包含nj个整数,表示每个人的起始城市。

输出格式

  第一行包含“distance = ”和一个整数,表示所租的车行驶的最小总距离。
  接下来nj行每行包含一个人的访问路线,城市按访问顺序给出并用“-”连接。
  存在多种方案时,选择需要访问到的城市集合元素最少的一种;仍然存在多种方案时,选择集合元素升序排列后字典序最小的一种。

样例输入

5 3 5
1 2 1
2 3 2
3 4 3
4 5 1
2 4 2
2
5 1

样例输出

distance = 6
5-4-2-3
1-2-3

样例输入

4 4 3
1 3 1
2 3 2
3 4 2
2
1 2

样例输出

distance = 5
1-3-4
2-3-4

样例输入

3 3 3
1 2 2
1 3 3
2 3 1
2
2 1

样例输出

distance = 3
2-3
1-2-3

数据规模和约定

  对于30%的数据,1 <= nc <= 8。
  对于100%的数据,1 <= nc <= 20,1 <= nj <= 10,1 <= dist <= 100。

 解题思路

只能得90分,有两组数据超时。先二进制枚举选城市(第i位为1则在之后的最小生成树(可能会有两棵甚至多棵树)第i个城市会出现,反之)。然后对每个人用bfs判断是否可以到达目的城市。同时记录最优解(经过的距离最小的路线最优,距离相同的路线经过城市最少的最优)。最后对最优解用bfs对每个人找路,找到后输出。

代码

/*
输入格式
  第一行包含三个整数nc, dc和nr,表示地图上的城市个数,目标城市的编号和地图上的道路条数。
  接下来nr行每行包含三个整数c1, c2和dist,表示一条长度为dist的双向道路(c1, c2)。
  接下来一行包含一个整数nj,表示人数。
  接下来一行包含nj个整数,表示每个人的起始城市。*/
#include <iostream>
#include <cmath>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
struct Road {
    int u,v,w,ord;  //起点 终点 权值 编号
}R[10005];
struct Through {
    int u,f,ord;
}T[10005];
int f[21];
int getf(int v)
{
    if(f[v] == v)
        return v;
    f[v] = getf(f[v]);
    return f[v];
}
bool Merge(int v1,int v2)
{
    int f1 = getf(v1);
    int f2 = getf(v2);
    if(f1 != f2) {
        f[f2] = f1;
        return true;
    }
    return false;
}
bool cmp (Road r1,Road r2)
{
    return r1.w < r2.w;
}
void show_way(int x)
{
    if(T[x].f == -1) {
        cout << T[x].u;
        return;
    }
    show_way(T[x].f);
    cout << "-" << T[x].u;
}
int start[11];  //每个人的起始城市
int main()
{
    //读入数据
    int nc,dc,nr;
    cin >> nc >> dc >> nr;
    for(int i=1;i<=nr;i++)
        cin >> R[i].u >> R[i].v >> R[i].w;
    int nj;
    cin >> nj;
    for(int i=1;i<=nj;i++)
        cin >> start[i];

    int most = pow(2,nc),Min_cost=0x3f3f3f3f,Min_pass=nc+1,FINNAL;    //最终方案
    bool flag;
    for(int c=1;c<=most;c++) {  //枚举每个city选或不选
        flag = false;
        for(int p=1;p<=nj;p++) {    //判断每个people的起始点是否包括在内
            if(!(c & (1 << (start[p]-1)))) {
                flag = true;
                break;
            }
        }
        if(flag)
            continue;
        if(!(c & (1 << (dc-1))))    //看目标城市是否包括在内
            continue;
        int thr = 0;
        for(int i=1;i<=nc;i++) {
            if(1 & (1 << (i-1)))        //看一共经过了多少城市
                thr++;
        }
        // Kruskal建立最小生成树并计算花费
        sort(R+1,R+1+nr,cmp);
        for(int i=1;i<=nc;i++)
            f[i] = i;
        int tot=0,pass=0,cost=0;
        bool con[21][21];
        bool book[401];
        memset(book,false,sizeof book);
        memset(con,0,sizeof con);
        for(int i=1;i<=nr;i++) {
            if(!(c & (1 << (R[i].u-1))) || !(c & (1 << (R[i].v-1))))    //如果这条路连接的两个城市至少有一个没包括
                continue;
            if(Merge(R[i].u,R[i].v)) {
                con[R[i].u][R[i].v] = true;
                con[R[i].v][R[i].u] = true; //在最小生成树中两边是直接连通的
                cost += R[i].w;
                //book[i] = true; //第i条边被选用
                ++pass;

            }
            if(pass == thr-1)
                break;
        }
        // 判断每个人是否能到达目标城市
        int i;
        for(i=1;i<=nj;i++) {
            bool book[401];
            memset(book,false,sizeof book);
            flag = false;
            if(start[i] == dc)
                continue;
            int k=0;
            queue<Through > Thr;
            T[k].u = start[i];
            T[k].f = -1;
            T[k].ord = k;
            book[start[i]] = true;
            Thr.push(T[k]);
            k++;
            while(!Thr.empty()) {
                int u = Thr.front().u;
                for(int x=1;x<=nc;x++) {
                    if(con[u][x] && !book[x]) {
                        if(x == dc) {
                            flag = true;
                            break;
                        }
                        book[x] = true;
                        T[k].u = x;
                        T[k].ord = k;
                        T[k].f = Thr.front().ord;
                        Thr.push(T[k]);
                        k++;
                    }
                }
                if(flag)
                    break;
                Thr.pop();
            }
            if(!flag)
                break;
        }
        if(i == nj+1) {
            if(Min_cost > cost) {
                Min_cost = cost;
                Min_pass = pass;
                FINNAL = c;
            }
            else if(Min_cost == cost) {
                if(Min_pass > pass) {
                    Min_pass = pass;
                    FINNAL = c;
                }
            }
        }

    }
    cout << "distance = " << Min_cost << endl;

    for(int i=1;i<=nc;i++)
        f[i] = i;
    bool con[21][21];

    memset(con,0,sizeof con);
    int pass = 0;
    for(int i=1;i<=nr;i++) {
        if(!(FINNAL & (1 << (R[i].u-1))) || !(FINNAL & (1 << (R[i].v-1))))    //如果这条路连接的两个城市至少有一个没包括
            continue;
        if(Merge(R[i].u,R[i].v)) {
            con[R[i].u][R[i].v] = true;
            con[R[i].v][R[i].u] = true; //在最小生成树中两边是直接连通的
            pass++;
        }
        if(pass == Min_pass)
            break;
    }

    // bfs找路
    for(int t=1;t<=nj;t++) {    // 每个人
        if(start[t] == dc) {
            cout << dc << endl;
            continue;
        }
        bool book[401];
        memset(book,false,sizeof book);
        queue<Through> Thr;
        int u = start[t];
        book[u] = true;
        int k=0;
        T[k].u = u;
        T[k].ord = k;
        T[k].f = -1;
        Thr.push(T[k]);
        k++;
        bool flag = false;
        while(!Thr.empty()) {
            u = Thr.front().u;
            for(int i=1;i<=nc;i++) {
                if( con[u][i] && !book[i]) {
                    book[i] = true;
                    T[k].u = i;
                    T[k].ord = k;
                    T[k].f = Thr.front().ord;
                    Thr.push(T[k]);
                    k++;
                    if(i == dc) {
                        flag = true;
                        break;
                    }
                }
                if(flag)
                    break;
            }
            if( flag)
                break;
            Thr.pop();
        }
        show_way(--k);
        cout << endl;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42765557/article/details/87967603