蓝桥杯 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

 

posted @ 2017-08-22 15:26  哇咔咔咔  阅读(220)  评论(0编辑  收藏  举报