牛マラソン
制限時間: 2000MS | メモリ制限: 30000K | |
総提出数: 9653 | 承認済み: 4411 | |
ケースの制限時間: 1000MS |
説明
米国での肥満の蔓延について聞いた後、ファーマージョンは牛にもっと運動をさせたいと考えているので、牛が走るための牛マラソンを作ることを約束しました。マラソンルートには、2つの農場と、それらの間の一連の道路で構成されるパスが含まれます。FJは、牛にできるだけ多くの運動をさせたいので、マップ上で互いに最も離れている2つの農場を見つけたいと考えています(距離は、2つの農場間の経路上の道路の全長で測定されます)。彼がこの最も遠い農場のペアの間の距離を決定するのを手伝ってください。
入力
* 1行目.....:「NavigationNightmare」と同じ入力形式。
出力
* 1行目:最も遠いファームのペア間の距離を示す整数。
サンプル入力
7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
サンプル出力
52
ヒント
最長のマラソンは、ファーム2から道路4、1、6、3を経由してファーム5まで続き、長さは20 + 3 + 13 + 9 + 7 = 52です。
ソース
質問の意味:木の直径を見つける、テンプレートの質問
木の直径を見つけるには2つの方法があります
(1)ツリーdpメソッドの説明:https://saoka.blog.luogu.org/shu-di-zhi-jing
配列定義:
dp [x]:xをルートとするサブツリー内のxからxまでの最も遠いノードからの距離
f [x]:ポイントxを通過するすべてのチェーンの中で最も長いチェーンの長さ
edge(u、v):ノードuからノードvまでのパスの長さ
アイデア:
-
リーフノードから開始し、上にマージして、各ノードxのd [x]を見つけます。特定の操作は、xの各エッジを列挙してから、遷移方程式を介して転送することができます。
-
次に、f [x]を見つける方法を検討します。V1とV2は、それぞれノードXから到達することができる最も遠いと第2遠隔ノードである場合、我々は簡単にXを通る最長鎖が(X、V1)及び(X、V2)によって決定されていることを得ることができからなる。したがって、d [x]を探しているときに、d [x]を更新できるポイントが見つかった場合は、これら2つのd []値を一緒に追加して、最も遠いポイントと2番目に遠いポイント。ポイント。
//树形dp
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int head[N], tot, ans;
int dp[N]; //以x为根的子树中,与x最远的节点到x的距离
bool vis[N];
struct Edge {
int to, w, next;
} edge[M];
void init() {
tot = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w) {
edge[tot].to = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot++;
}
void dfs(int u) {
vis[u] = 1;
for(int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if(vis[v]) continue;
dfs(v);
//设v1、v2分别为从节点x出发能走到的最远、次远节点,那么经过x的最长链是由(x,v1)和(x,v2)组成的
//在求d[x]的时候,如果找到了一个点能够更新d[x], 那么就把这两个d[]值相加,这样一定可以保证能够取到最远点和次远点
ans = max(ans, dp[u] + dp[v] + edge[i].w);
dp[u] = max(dp[u], dp[v] + edge[i].w);
}
}
int main() {
char c;
int n, m, u, v, w;
init();
scanf("%d%d", &n, &m);
while(m--) {
scanf("%d%d%d", &u, &v, &w);
cin >> c;
addedge(u, v, w);
addedge(v, u, w);
}
for(int i = 1; i <= n; ++i) vis[i] = 0;
for(int i = 1; i <= n; ++i) dp[i] = 0;
dfs(1);
printf("%d\n", ans);
return 0;
}
(2)説明を求める2回のdfs:https://www.cnblogs.com/handsome-zyc/p/11237529.html
方法:任意の点Pから開始し、そこから最も遠い点Qを見つけ、次に点Qから開始し、それから最も遠い点Wを見つけます。WからQまでの距離は、
証明は次のとおりです。
①Pがすでに直径上にある場合、木の直径の定義によれば、Qも直径上にあり、直径の終点であることがわかります。
②Pが直径上にない場合は、現時点ではWQが直径ではなく、ABが直径であると仮定して、矛盾法を使用します。
---> ABとPQに交点Cがある場合、PはQから最も遠いため、PC + CQ> PC + CA、つまりCQ> CAであるため、CQ + CB> CA + CBを簡単に取得できます。 、CQ + CB> AB、次の図に示すように、ABの直径と矛盾しますが、これは正しくありません(ABとPQは必ずしも直線ではなく、便宜上描画されています)。
---> ABとPQの間に交点がない場合、MはAB上の任意の点であり、NはPQ上の任意の点です。まず、NP + NQ> NQ + MN + MBのままですが、NQを引くと、NP> MN + MBになります。NP+ MN> MBであることが簡単にわかるので、NP + MN + MA> MB + MA、
つまり、NP + MN + MA> ABであり、ABは直径の矛盾であるため、次の図に示すように、この状況は当てはまりません。
//两次dfs
//先从任意一点P出发,找离它最远的点Q,再从点Q出发,找离它最远的点W,W到Q的距离就是是的直径
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int head[N], tot, dis[N], ans, id;
struct Edge {
int to, w, next;
} edge[M];
void init() {
tot = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w) {
edge[tot].to = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot++;
}
void dfs(int u, int fa) {
if(ans < dis[u]) {
ans = dis[u];
id = u;
}
for(int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if(v == fa) continue;
dis[v] = dis[u] + edge[i].w;
dfs(v, u);
}
}
int main() {
char c;
int n, m, u, v, w;
init();
scanf("%d%d", &n, &m);
while(m--) {
scanf("%d%d%d", &u, &v, &w);
cin >> c;
addedge(u, v, w);
addedge(v, u, w);
}
for(int i = 1; i <= n; ++i) dis[i] = 0;
ans = 0;
dfs(1, 0);
ans = 0, dis[id] = 0;
dfs(id, 0);
printf("%d\n", ans);
return 0;
}