4小时55分敲完代码,0调试,跑出样例直接交了,结果爆栈,扩栈交了一发,居然A了,我和我的小伙伴们都惊呆了。。。
题意:给出有n个节点的一棵树,树上的边有权值。我们切断一条边,将整棵树分成两颗,计算一个值,这个值的计算方法,v = b * max ( d1 , d2 ) ;其中,b为所切的边的权值,d1 ,d2 为切断后形成的两颗树的树上的最长路。对于每一条边,会计算出一个v值,问切那条边时,v值最小,如果有多个最小的v值,输出边的id最小的那条。
解题思路:树形dp(对于整棵树而言,根节点为1号节点)。
。对于每一个节点,要记录很多东西,l1 , l2 , l3 分别表示该节点下,最长的,次长的,第三长链的长度,f1 , f2 , f3分别表示最长的,次长的,第三长的链来自于哪个儿子节点。t1 , t2分别表示该节点下的儿子中的最长路和次长路的长度,g1 , g2分别表示最长路,次长路的儿子是哪个儿子。dp[u]表示u节点下的这颗子树的最长路。怎么更新这些信息呢?对于l1 , l2 , l3 , f1 , f2 , f3 枚举每个儿子,递推上来吧,还是很好做的,t1 , t2 就枚举 dp[v] (v表示u的儿子节点)得出来。dp[u]就是max ( t1 , t2 , l1 + l2 ) 。这就可以推出这棵树上每个节点的信息了。然后我么就要算切断某一条边的时候的值了。对于根节点下的边,我们枚举一下,答案还是很容易得出的,d1就是dp[v], d2的话,看枚举的这个v是不是g1,能取t1就取 t1 , 否则就取t2,但这样d2并不一定是最优的,还要从最长,次长,第三长里面取出能去的最长的两条去拼一下,得出一个最大值就是d2了。对于每个节点v,算一次值,更新下答案。然后就要处理以1的儿子为树根了,这是就要把父亲这个节点转变成儿子了,也就是我们要处理的下一个树根多了一个特殊的儿子,就是原来的它的父亲,我们对这个父亲,转变成儿子,要传两个信息下去,它能传下去的最长的l(就是链)的值,和他传下去的最长的t(就是将它变成儿子后,能得到的新的最长路值),而这两个信息,又要根据之前预处理的那些东西进行计算了,链的值就是从能取的最长的链里取一个最长的+1,最长路的值,就是根据能取的儿子的最长路里的值,以及能去的最长的两条边拼出的最大值(这就是为什么要处理三条最长的链了,因为最长的链有可能来自于我要转换成根的那个儿子,而这是不能取的)。转变了树的结构后,我们对于新的根节点,又可以计算它下面的每条边能计算的v值,一直递归,计算出每条边的v值就好了。代码:
#pragma comment(linker, "/STACK:10600000,10600000")
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100005;
struct Edge {
int v, next, w;
Edge(int v, int w, int next) :
v(v), w(w), next(next) {
}
Edge() {
}
} edge[maxn << 1];
int head[maxn], E, n;
void init() {
memset(head, -1, sizeof(head));
E = 0;
}
void add(int s, int t, int w) {
edge[E] = Edge(t, w, head[s]);
head[s] = E++;
edge[E] = Edge(s, w, head[t]);
head[t] = E++;
}
int v1[maxn], v2[maxn]; //节点
int f1[maxn], f2[maxn]; //记录是哪个儿子更新的节点信息
int l1[maxn], l2[maxn], l3[maxn]; // 链
int g1[maxn], g2[maxn], g3[maxn]; //记录是哪个儿子更新的链信息
int dp[maxn];
void dfs1(int u, int fa) {
int i;
l1[u] = l2[u] = l3[u] = v1[u] = v2[u] = 0;
dp[u] = 0;
for (i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].v;
if (v == fa)
continue;
dfs1(v, u);
if (v2[u] < dp[v]) { //节点
v2[u] = dp[v];
f2[u] = v;
if (v1[u] < v2[u]) {
swap(v1[u], v2[u]);
swap(f1[u], f2[u]);
}
}
if (l3[u] < l1[v] + 1) { // 链
l3[u] = l1[v] + 1;
g3[u] = v;
if (l2[u] < l3[u]) {
swap(l2[u], l3[u]);
swap(g2[u], g3[u]);
}
if (l1[u] < l2[u]) {
swap(l1[u], l2[u]);
swap(g1[u], g2[u]);
}
}
}
dp[u] = max(l1[u] + l2[u], v1[u]);
}
int ans, pos;
int tp[7];
void dfs2(int u, int fa, int fv, int fl) { // fv节点信息,fl链信息
int i;
for (i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].v;
if (v == fa) continue;
//链
int tt = 0;
if (g1[u] != v)
tp[tt++] = l1[u];
if (g2[u] != v)
tp[tt++] = l2[u];
if (g3[u] != v)
tp[tt++] = l3[u];
tp[tt++] = fl;
sort(tp, tp + tt);
int fll = tp[tt-1]+1;
//节点
int d = fv;
if (f1[u] != v) d = max(d, v1[u]);
if (f2[u] != v) d = max(d, v2[u]);
d = max(d, tp[tt - 1] + tp[tt - 2]);
int fvv = d;
d = max(d, dp[v]);
d *= edge[i].w;
if (ans > d || (ans == d && pos > i)) {
ans = d;
pos = i;
}
dfs2(v, u, fvv, fll);
}
}
int x, y, z;
int main() {
int i, cas;
scanf("%d", &cas);
for (int ca = 1; ca <= cas; ca++) {
scanf("%d", &n);
init();
for (i = 0; i < n - 1; i++) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
}
dfs1(1, -1);
ans = 1000000009;
dfs2(1, -1, 0, 0);
printf("Case #%d: %d\n", ca, (pos>>1)+1);
}
return 0;
}