I Wanna Go Home--一道编程题引发的思考以及代码记录

这是牛客网上见到的一到编程题,题目如下:

题目描述:
The country is facing a terrible civil war—-cities in the country are divided into two parts supporting different leaders. As a merchant, Mr. M does not pay attention to politics but he actually knows the severe situation, and your task is to help him reach home as soon as possible. “For the sake of safety,”, said Mr.M, “your route should contain at most 1 road which connects two cities of different camp.” Would you please tell Mr. M at least how long will it take to reach his sweet home?
输入描述:
The input contains multiple test cases.
The first line of each case is an integer N (2<=N<=600), representing the number of cities in the country.
The second line contains one integer M (0<=M<=10000), which is the number of roads.
The following M lines are the information of the roads. Each line contains three integers A, B and T, which means the road between city A and city B will cost time T. T is in the range of [1,500].
Next part contains N integers, which are either 1 or 2. The i-th integer shows the supporting leader of city i.
To simplify the problem, we assume that Mr. M starts from city 1 and his target is city 2. City 1 always supports leader 1 while city 2 is at the same side of leader 2.
Note that all roads are bidirectional and there is at most 1 road between two cities.
Input is ended with a case of N=0.
输出描述:
For each test case, output one integer representing the minimum time to reach home.
If it is impossible to reach home according to Mr. M’s demands, output -1 instead.
示例1
  输入

2
1
1 2 100
1 2
3
3 
1 2 100
1 3 40 
2 3 50
1 2 1 
5 
5
3 1 200 
5 3 150
2 5 160
4 3 170
4 2 170
1 2 2 2 1
0

 输出

100
90
540

***********************************

下面来记录下这道题的解题思路

  • 首先这道题比较长,我们可以把题目简单理解为:多个结点(这些结点只有两种形状-要么方要么圆)组成的权重图中,1号2号两个不同形状结点之间的最短路径,且该路径上最多只能有一条连接两个不同形状的结点的边。
  • 可以看出这是加了一定限制条件的dijkstra最短路径问题。
    dijkstra复习参考 https://www.cnblogs.com/nigang/p/3658990.html
    此时抓住限定条件:该路径上最多只能有一条连接两个不同形状的结点的边+起末点1号2号是两个不同形状的结点,可以得出最短路径上一定有且只有一条边是连接两个不同形状的边。
  • 具体求解方法就是先用Dijkstra算出从1号点出发(只能经过同形状点或者直达)到所有同形状点的最短距离,同理求解2号点出发对所有同形状点的最短距离,之后枚举除1号2号点外存在不同形状结点相连的边(i, j), 该条路径的最小距离就是dist[1][i] + dist[2][j] + w(i, j)(其中i与1同形状,j与2同形状)中的最小值。

最后看下代码(摘自牛客网)

链接:https://www.nowcoder.com/questionTerminal/0160bab3ce5d4ae0bb99dc605601e971
来源:牛客网

#include <stdio.h>
#include <limits.h>
#define N 601

int G[N][N];//城市间的距离,用时间表示
int camp[N];//各个城市的阵营
int n[3];//n[0]: 城市数量; n[1]、n[2]: 两个阵营各自的城市数量

int Add(int a, int b)
{//两个数相加
    if(a==INT_MAX||b==INT_MAX) return INT_MAX;
    else return a+b;
}

int Add3(int a, int b, int c)
{//三个数相加
    if(a==INT_MAX||b==INT_MAX||c==INT_MAX)  return INT_MAX;
    else return a+b+c;
}

int d1[N], d2[N];//分别存储1、2到同阵营各点最小距离
bool mark[N];//标记点是否在集合里面
void Dijkstra(int d[N], int from)
{//dijkstra算法求某点到同阵营各点的最小距离
    for(int i=0; i<=n[0]; i++)
    {//不是同阵营的先塞进集合里面
        if(camp[i]==from) mark[i]=false;
        else mark[i]=true;
    }
    mark[from]=true;//把起点塞进集合
    for(int i=0; i<=n[0]; i++)//初始化d[N]
    {
        if(camp[i]==from) d[i]=G[from][i];
    }
    for(int i=0; i<n[camp[from]]-1; i++)//把同阵营的点全都塞进集合
    {
        bool isFirst=true;
        int near;
        for(int i=1; i<=n[0]; i++)//挑一个d[?]最小的,下标为near
        {
            if(mark[i]==false)
            {
                if(isFirst)
                {
                    near=i;
                    isFirst=false;
                }
                else if(d[i]<d[near]) near=i;
            }
        }
        mark[near]=true;//加入集合
        for(int i=1; i<=n[0]; i++)//更新同阵营的点的d[i]信息
        {
            if(camp[i]==from&&mark[i]==false)
            {
                int sum=Add(d[near], G[near][i]);
                if(sum<d[i]) d[i]=sum;
            }
        }
    }
}

int main()
{
    int m;
    while(scanf("%d", &n[0])!=EOF)
    {
        if(n[0]==0) break;
        scanf("%d", &m);
        for(int i=0; i<=n[0]; i++)//初始化邻接矩阵
        {
            for(int j=0; j<=n[0]; j++)
            {
                if(i==j) G[i][j]=0;
                else G[i][j]=INT_MAX;
            }
        }
        while(m--)//读取边
        {
            int a, b, t;
            scanf("%d%d%d", &a, &b, &t);
            if(t<G[a][b]) G[a][b]=G[b][a]=t;
        }
        n[1]=n[2]=0;//两个阵营暂时都没城市
        for(int i=1; i<=n[0]; i++)//读取阵营信息
        {
            scanf("%d", &camp[i]);
            n[camp[i]]++;
        }
        Dijkstra(d1, 1);//求出1到同阵营各点的最小距离
        Dijkstra(d2, 2);//求出2到同阵营各点的最小距离
        int ans=INT_MAX;//先假设最小距离是无穷大
        for(int i=1; i<=n[0]; i++)//开始找穿越边境的路
        {
            for(int j=i+1; j<=n[0]; j++)
            {
                if(camp[i]==1&&camp[j]==2)//i是1阵营j是2阵营的话
                {//看看从这条路过境是否更划算
                    int sum=Add3(d1[i], d2[j], G[i][j]);
                    if(sum<ans) ans=sum;
                }
                else if(camp[i]==2&&camp[j]==1)//反之
                {
                    int sum=Add3(d2[i], d1[j], G[i][j]);
                    if(sum<ans) ans=sum;
                }
            }
        }
        if(ans!=INT_MAX) printf("%d\n", ans);//输出结果
        else printf("-1\n");//最短总耗时是无穷,说明无路可走
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39860046/article/details/81908266