蓝桥杯 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
边完善自己边认识自己