トピックリンク
件名の説明:
エッジによってn個の点のn-1の通信は、エッジが削除された場合、直径は、図中の最小の数であり、存在します。
説明を入力します。
正の整数、N-(N - <= 10の最初の行6に)、ポイントの数を表します。そして、これらの点は、1からnまでの番号が付けられています。
次に、N-1行、各行3つの正の整数W、B、。その数は、ポイントの数であり、b点間の長さ(W <= 1000)面wがあるています。
出力説明:
問題の要件を満たすために、整数の行を入力します。
最小直径の後端縁を除去するために、我々は、エッジ除去さの直径に画像を選択します。列挙は、各エッジで除去し、最小直径を更新し、我々が事前にこれらを取得する方法を見つけて、直径に保存されているので、我々は計算された直径を列挙するために持っているすべての時間は、確かに、オーバーラン時間になることに留意すべきです。
そして、問題の解決策があります:
まず、一般的な直径を解決するために二つの方法を習得する(2回DFSツリーDP)
方法の一つ:二回DFS(注:この方法は、点Lの直径の両端に得ることができ、R)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 1;
struct edge { //链式前向星
int to, next, w; edge(){}
edge(int a,int b,int c):to(a),next(b),w(c){}
}e[N << 1]; int tot, head[N];
int d[N];// 记录节点到根的距离
void dfs(int x, int pre, int &lr) {
if (d[lr] < d[x]) lr = x;
for(int i = head[x]; i != -1; i = e[i].next) {
int to = e[i].to;
if (to == pre) continue;
d[to] = d[x] + e[i].w;
dfs(to, x, lr);
}
}
int main() {
int n; scanf("%d", &n);
memset(head, -1, sizeof(head));
for(int i = 1; i < n; ++i) {
int x,y,k; scanf("%d%d%d", &x, &y, &k);
e[++tot]=edge(y,head[x],k),head[x]=tot;
e[++tot]=edge(x,head[y],k),head[y]=tot;
}
int l,r; dfs(1,0,l=1); d[l]=0;dfs(l,0,r=l);
printf("%d\n", d[r]);
return 0;
}
方法2:木のDP(追加:あなたはすべてのサブ直径の木を得ることができます)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 1;
struct edge { //链式前向星
int to, next, w; edge(){}
edge(int a,int b,int c):to(a),next(b),w(c){}
}e[N << 1]; int tot, head[N];
int f1[N], f2[N];// 以i为根的直径和最长链
void dp(int x, int pre) {// 树形dp
for(int i = head[x]; i != -1; i = e[i].next) {
int to = e[i].to;
if (to == pre) continue;// 防止回头
dp(to, x);
f1[x] = max(f1[x], max(f1[to], f2[x] + f2[to] + e[i].w));
if (f2[x] < f2[to] + e[i].w) f2[x] = f2[to] + e[i].w;
}
}
int main() {
int n; scanf("%d", &n);
memset(head, -1, sizeof(head));
for(int i = 1; i < n; ++i) {
int x,y,k; scanf("%d%d%d", &x, &y, &k);
e[++tot]=edge(y,head[x],k),head[x]=tot;
e[++tot]=edge(x,head[y],k),head[y]=tot;
}
dp(1, 0); printf("%d\n", f1[1]);
return 0;
}
二つの方法は、我々はそのすべてのサブツリー、そのすべてのサブツリーの記録のルート直径のため、その後Rの共感をルート径Lを記録するためにそれらを組み合わせることができ、相補的であり、そして最終的に我々は列挙するとき分析と直径が直接更新することができます。具体的なコードは次のよう:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6+1;
const int inf = 0x3f3f3f3f;
template<class T> inline void read(T &x,int xk=10) { // quick read
char ch = getchar();T f = 1;
for(x =0;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') f=-1;
for(;ch<='9'&&ch>='0';ch=getchar()) x=x*xk+ch-'0';x*=f;
}
struct edge { //链式前向星
int to, next, w; edge(){}
edge(int a,int b,int c):to(a),next(b),w(c){}
}e[N << 1]; int tot, head[N];
int d[N],f1[N],f2[N],D[N],res = inf;
void dfs(int x, int pre, int &lr) {
if (d[lr] < d[x]) lr = x;
for(int i = head[x]; i != -1; i = e[i].next) {
int to = e[i].to;
if (to == pre) continue;
d[to] = d[x] + e[i].w;
dfs(to, x, lr);
}
}
void dpl(int x, int pre) {
for(int i = head[x]; i != -1; i = e[i].next) {
int to = e[i].to;
if (to == pre) continue;
dpl(to, x);
f1[x]=max(f1[x],max(f1[to],f2[x]+f2[to]+e[i].w));
if (f2[x]<f2[to]+e[i].w) f2[x] = f2[to] + e[i].w;
if (D[x] < f1[x]) D[x] = f1[x];
}
}
void dpr(int x, int pre) {
for(int i = head[x]; i != -1; i = e[i].next) {
int to = e[i].to;
if (to == pre) continue;
dpr(to, x);
f1[x]=max(f1[x],max(f1[to],f2[x]+f2[to]+e[i].w));
if (f2[x]<f2[to]+e[i].w) f2[x] = f2[to] + e[i].w;
if (D[pre] < f1[x]) D[pre] = f1[x];
}
}
bool Dfs(int x, int pre, int y) {
if (x == y) return true;
for(int i = head[x]; i != -1; i = e[i].next) {
int to = e[i].to;
if (to == pre) continue;
if (Dfs(to, x, y)) {
if (res > D[x]) res = D[x];
return true;
}
}
return false;
}
int main() {
int n; read(n);
memset(head, -1, sizeof(head));
for(int i = 1; i < n; ++i) {
int x, y, k; read(x); read(y); read(k);
e[++tot]=edge(y,head[x],k),head[x]=tot;
e[++tot]=edge(x,head[y],k),head[y]=tot;
}
int l, r; dfs(1, 0, l = 1); d[l] = 0; dfs(l, 0, r = l);
dpl(l,0);memset(f1,0,sizeof(f1));memset(f2,0,sizeof(f2));dpr(r,0);
Dfs(l, 0, r); printf("%d\n", res);
return 0;
}