BZOJ1509 [NOI2003]逃学的小孩 树型DP
题目:
分析:
首先明确我们是要求 min(dist[C][A],dist[C][B])+dist[A][B].
我们把C当成树根,第一我们可以发现min里面取dist[C][A]或者dist[C][B]其实是一个意思(因为可以交换)。
接着可以发现dist[A][B]实际上是这棵树的直径。如果不是,那么答案一定不是最优的。我们可以这样去想:
如果dist[A][B]不是直径,那么一定有dist[C][A']使得比dist[C][A]更优,而且A'一定是直径的一个端点:-)。加号的前面和后面都不是最优的,那么答案也不是最优的。
所以我们可以先处理出树的直径,接着枚举点C使得它到两个端点的距离的最小值最大。
时间复杂度O(n),空间复杂度O(n)。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<vector> 6 using namespace std; 7 8 typedef long long ll; 9 10 struct edge{ 11 ll to,w; 12 }; 13 14 const ll maxn = 200010; 15 16 ll n,m; 17 vector <edge> g[maxn]; 18 ll arr[maxn],dep[maxn]; 19 20 void dfs(int now,ll data){ 21 arr[now] = 1; 22 dep[now] = min(dep[now],data); 23 for(int i=0;i<g[now].size();i++){ 24 if(arr[g[now][i].to]) continue; 25 dfs(g[now][i].to,g[now][i].w+data); 26 } 27 arr[now] = 0; 28 } 29 30 int get_max(){ 31 dep[0] = 0; 32 int maxx = 0; 33 for(int i=1;i<=n;i++){ 34 if(dep[maxx] < dep[i]) maxx = i; 35 } 36 return maxx; 37 } 38 39 void read(){ 40 scanf("%lld%lld",&n,&m); 41 for(ll i=1;i<=m;i++){ 42 ll x,y,c; scanf("%lld%lld%lld",&x,&y,&c); 43 g[x].push_back((edge){y,c}); 44 g[y].push_back((edge){x,c}); 45 } 46 } 47 48 void work(){ 49 memset(dep,127/3,sizeof(dep)); 50 dfs(1,0);//get the farthest point in tree 51 ll ans=0,t1 = get_max(); 52 memset(dep,127/3,sizeof(dep)); 53 dfs(t1,0);//another point 54 ll t2 = get_max(); 55 ans += dep[t2];//zhijing 56 dfs(t2,0); 57 ll maxx = 0; 58 for(int i=1;i<=n;i++) 59 maxx = max(maxx,dep[i]); 60 ans += maxx; // farthest dian for zhijing 61 printf("%lld",ans); 62 } 63 64 int main(){ 65 read(); 66 work(); 67 return 0; 68 }