蓝桥杯 T32 [大臣的旅费] DFS,Dijsktra,直径

#include <iostream>
#include <vector>
#include <memory.h>
#include <queue>
#include <stdio.h>
using namespace std;
typedef long long ll;
const int MAX=1e5+10;
const int inf=1e9;

struct node{
    int dest;
    int dis;
    node(){}
    node(int a,int b):dest(a),dis(b){} 
}; 

bool operator <(const node&a,const node&b){
    return a.dis>b.dis;
}

vector<node>MAP[MAX]; //邻接表 
int n; 


bool sure[MAX];
int dis[MAX];
void Dijkstra(int x){//邻接表描述 
    memset(sure,0,sizeof(sure));
    for(int i=1;i<=n;i++)dis[i]=(i==x?0:inf);
    priority_queue<node>q;
    q.push(node{x,0});
    node nw;
    while(!q.empty()){
        nw=q.top();
        q.pop();
        if(!sure[nw.dest]){
            sure[nw.dest]=true;
            for(int i=0;i<MAP[nw.dest].size();i++){
                if(nw.dis+MAP[nw.dest][i].dis<dis[MAP[nw.dest][i].dest]){
                    dis[MAP[nw.dest][i].dest]=nw.dis+MAP[nw.dest][i].dis;
                    q.push(node{MAP[nw.dest][i].dest,dis[MAP[nw.dest][i].dest]});
                }
            }
        }
    }
    
} 

int main(){
    int x,y,d;
    scanf("%d",&n);
    memset(MAP,0,sizeof(MAP)); 
    for(int i=1;i<=n-1;i++){
        scanf("%d%d%d",&x,&y,&d);
        MAP[x].push_back(node{y,d});
        MAP[y].push_back(node{x,d});
    }
    Dijkstra(1);
    ll maxV=0,maxID;
    for(int i=2;i<=n;i++){
        if(dis[i]>maxV){
            maxV=dis[i];
            maxID=i;
        }
    }
    Dijkstra(maxID);
    for(int i=1;i<=n;i++){
        if(i==maxID)continue;
        if(dis[i]>maxV){
            maxV=dis[i];
        }
    }
    
    maxV=(maxV*maxV+21*maxV)/2;
    printf("%lld\n",maxV);
    return 0;
} 

 

题目链接:http://lx.lanqiao.cn/problem.page?gpid=T32

题目大意:有N个城市,城市间的道路构成一棵树,让我们求任意两点间距离的最大值,据此算出路费。

关键思想:其实就是让我们求树的直径,求树的直径有这样一种方法,从根开始DFS到最远的一个城市,这个城市必然是直径的一个端点,然后从这个端点开始DFS到最远的一个城市,此路径长度就是直径。

我想的证明是这样的——

  首先,我们从顶点O开始DFS到最远的城市A,如果A不是直径的一个端点,那就有另一个城市B是直径的端点。

  我们来考虑A,B,显然他们是有公共祖先的,树上除了根节点的任意两点都有公共祖先,不妨设为F。

  从O到F的距离在OA和OB两条路径上都算过了,接着我们来看FA和FB路径,既然A离O最远,那FA应该是大于FB的。

  我们看直径的另一个端点G,直径应该是GFB对吧,但是既然GA大于FB,那么GFA就大于GFB,GFB就成最长的了,这和GFA是直径矛盾。

总的来说,我们只要求两次最远就可以了。

 

代码如下:

 法一:两次DFS

#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 1e5 + 10;
struct node{
    int dest;
    int dis;
    node(){}
    node(int a, int b) :dest(a), dis(b){}
};

vector<node> MAP[MAXN];
bool vis[MAXN];
int ans;
int ansID;

void DFS(int x, int d){
    if (d>ans){
        ans = d;
        ansID = x;//打死不能加return
    }
    for (int i = 0; i<MAP[x].size(); i++){
        if (!vis[MAP[x][i].dest]){
            vis[MAP[x][i].dest] = true;
            DFS(MAP[x][i].dest, d + MAP[x][i].dis);
            vis[MAP[x][i].dest] = false;
        }
    }
    return;
}

int main(){
    int n;
    cin >> n;
    int x, y, d;
    for (int i = 0; i<n - 1; i++){
        cin >> x >> y >> d;
        MAP[x].push_back(node{ y, d });
        MAP[y].push_back(node{ x, d });
    }
    ans = 0;
    vis[1] = true; DFS(1, 0); vis[1] = false;//注意起点vis的处理
    vis[ansID] = true; DFS(ansID, 0); vis[ansID] = false;
    cout << (ans*ans + 21 * ans) / 2 << endl;

    return 0;
}

 法二:两次Dijsktra

 

转载于:https://www.cnblogs.com/G-M-WuJieMatrix/p/7411430.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值