去年蓝桥杯结束以后基本就没有在oj上刷题了,最近突然被老师拉去要打JSCPC(江苏省大学生程序设计大赛),只能在hdu上找找以前ccpc的题目刷刷,顺便复习复习(而且估计也没时间刷那种比较水的题了)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5834
参考题解:http://www.cnblogs.com/WABoss/p/5771931.html
题意和一些分析上面的题解有,里面给出的是c++的实现。我感觉这个题解已经算是网上对于这道题分析里面最详细的了,但是我其实还是不太理解,硬着头皮把题解里面的c++代码翻译成java然后再研究,大致才有了感觉,大体如下(很多还是实在说不清楚,需要自己在草稿纸上画画体会):
- dp_down[0/1][u]:u结点往其为根的子树走,并且不走回来/走回来,能得到的最大权值
- dp_up[0/1][u]:u结点往其父亲向上走,并且不走回来/走回来,能得到的最大权值
关于d_down[0][u]部分的代码
d_down[0][u]=d_down[1][u]=val[u]; for (int i=head[u];i!=-1;i=edges[i].next) { int v=edges[i].v; if (v==fa) continue; dfs1(v, u); if (d_down[0][v]-2*edges[i].w>0) d_down[0][u]+=d_down[0][v]-2*edges[i].w; }这里v是u的儿子,显然如果沿着v走下去再走回来能够赚钱,那么就要把v为根的这个子树在计算代价时选进去
关于d_down[1][u]部分的代码
int mx=0; for (int i=head[u];i!=-1;i=edges[i].next) { int v=edges[i].v; if (v==fa) continue; if (d_down[0][v]-2*edges[i].w>0) { mx=max(mx, (d_down[1][v]-edges[i].w)-(d_down[0][v]-2*edges[i].w)); } else { mx=max(mx, d_down[1][v]-edges[i].w); } } d_down[1][u]=d_down[0][u]+mx;这里着重考虑最后要从哪个儿子v往下走不回来的问题,分两种情况考虑,一种是v在之前求d_down[0][u]的策略
中被用到的情况,一种是没被用到的情况,然后进行讨论,取最大
int mx1=0,mx2=0,tmp; for (int i=head[u];i!=-1;i=edges[i].next) { int v=edges[i].v; if (v==fa) continue; if (d_down[0][v]-2*edges[i].w>0) tmp=(d_down[1][v]-edges[i].w)-(d_down[0][v]-2*edges[i].w); else tmp=d_down[1][v]-edges[i].w; if (mx1<tmp) { mx2=mx1; mx1=tmp; } else if (mx2<tmp) { mx2=tmp; } }以上代码求了一个最大值一个次大值,在求d_up[1][v]时会使用到
关于d_up[0][v]部分的代码
int v=edges[i].v; if (v==fa) continue; int tmp2; if (d_down[0][v]-2*edges[i].w>0) tmp2=d_down[0][u]-(d_down[0][v]-2*edges[i].w); else tmp2=d_down[0][u]; int mx=max(d_up[0][u]-2*edges[i].w,tmp2-2*edges[i].w); int tmp3=d_up[0][u]+tmp2-2*edges[i].w-val[u]; mx=max(mx, tmp3); d_up[0][v]=val[v]+max(0, mx);这个是对于从v的父亲u,考虑单纯从以u为根的子树往下走再走回u再走回v,单纯u结点往其父亲方向走再走回u再走回v,以及u两个方向都走一遍再回到v这三种情况进行讨论(注意这里从u往下走再走回来是不包括走以v为根的这个子树的情况的)
关于d_up[1][v]部分的代码
if (d_down[0][v]-2*edges[i].w>0) { if (mx1==(d_down[1][v]-edges[i].w)-(d_down[0][v]-2*edges[i].w)) tmp=d_down[1][u]-(d_down[1][v]-edges[i].w)+mx2; else tmp=d_down[1][u]-(d_down[0][v]-2*edges[i].w); } else if (d_down[1][v]-edges[i].w>0) { if (mx1==d_down[1][v]-edges[i].w) tmp=d_down[1][u]-(d_down[1][v]-edges[i].w)+mx2; else tmp=d_down[1][u]; } else tmp=d_down[1][u]; mx=max(d_up[1][u]-edges[i].w, tmp-edges[i].w); mx=max(mx, max(d_up[0][u]+tmp-edges[i].w-val[u],d_up[1][u]+tmp2-edges[i].w-val[u])); d_up[1][v]=val[v]+max(0, mx);这个主要是考虑v走到u以后往其父亲方向走不回来,沿以u为根的子树往下走而且不回来,以及到了u以后先往下走再往上走不回来,以及先往上走再往下走不回来这样四种情况(然后注意对上面最大值和次大值应用的理解~~~)
最后的结果输出(如果我上面写的代码能理解的话,这个结果表示应该也不难理解)
for (int i=1;i<=n;i++) System.out.println(max(d_up[0][i]+d_down[1][i], d_up[1][i]+d_down[0][i])-val[i]);
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.StringTokenizer; class Reader{ static BufferedReader reader; static StringTokenizer tokenizer; static void init(InputStream input) { reader=new BufferedReader(new InputStreamReader(input)); tokenizer=new StringTokenizer(""); } static String next() throws IOException{ while (!tokenizer.hasMoreTokens()) { tokenizer=new StringTokenizer(reader.readLine()); } return tokenizer.nextToken(); } static int nextInt() throws IOException { return Integer.parseInt(next()); } } class Edge{ int v,w,next; /** * @param v * @param w * @param next */ public Edge(int v, int w, int next) { super(); this.v = v; this.w = w; this.next = next; } /** * */ public Edge() { super(); // TODO Auto-generated constructor stub } /** * @return the v */ public int getV() { return v; } /** * @param v the v to set */ public void setV(int v) { this.v = v; } /** * @return the w */ public int getW() { return w; } /** * @param w the w to set */ public void setW(int w) { this.w = w; } /** * @return the next */ public int getNext() { return next; } /** * @param next the next to set */ public void setNext(int next) { this.next = next; } } public class Main { static int t,n,u,v,c,cnt; static int head[],val[]; static int maxn=200000; static Edge edges[]=new Edge[2*maxn]; static int d_down[][]=new int[3][maxn]; static int d_up[][]=new int[3][maxn]; static int max(int a,int b) { return a>b?a:b; } static void dfs1(int u,int fa) { d_down[0][u]=d_down[1][u]=val[u]; for (int i=head[u];i!=-1;i=edges[i].next) { int v=edges[i].v; if (v==fa) continue; dfs1(v, u); if (d_down[0][v]-2*edges[i].w>0) d_down[0][u]+=d_down[0][v]-2*edges[i].w; } int mx=0; for (int i=head[u];i!=-1;i=edges[i].next) { int v=edges[i].v; if (v==fa) continue; if (d_down[0][v]-2*edges[i].w>0) { mx=max(mx, (d_down[1][v]-edges[i].w)-(d_down[0][v]-2*edges[i].w)); } else { mx=max(mx, d_down[1][v]-edges[i].w); } } d_down[1][u]=d_down[0][u]+mx; } static void dfs2(int u,int fa) { int mx1=0,mx2=0,tmp; for (int i=head[u];i!=-1;i=edges[i].next) { int v=edges[i].v; if (v==fa) continue; if (d_down[0][v]-2*edges[i].w>0) tmp=(d_down[1][v]-edges[i].w)-(d_down[0][v]-2*edges[i].w); else tmp=d_down[1][v]-edges[i].w; if (mx1<tmp) { mx2=mx1; mx1=tmp; } else if (mx2<tmp) { mx2=tmp; } } for (int i=head[u];i!=-1;i=edges[i].next) { int v=edges[i].v; if (v==fa) continue; int tmp2; if (d_down[0][v]-2*edges[i].w>0) tmp2=d_down[0][u]-(d_down[0][v]-2*edges[i].w); else tmp2=d_down[0][u]; int mx=max(d_up[0][u]-2*edges[i].w,tmp2-2*edges[i].w); int tmp3=d_up[0][u]+tmp2-2*edges[i].w-val[u]; mx=max(mx, tmp3); d_up[0][v]=val[v]+max(0, mx); if (d_down[0][v]-2*edges[i].w>0) { if (mx1==(d_down[1][v]-edges[i].w)-(d_down[0][v]-2*edges[i].w)) tmp=d_down[1][u]-(d_down[1][v]-edges[i].w)+mx2; else tmp=d_down[1][u]-(d_down[0][v]-2*edges[i].w); } else if (d_down[1][v]-edges[i].w>0) { if (mx1==d_down[1][v]-edges[i].w) tmp=d_down[1][u]-(d_down[1][v]-edges[i].w)+mx2; else tmp=d_down[1][u]; } else tmp=d_down[1][u]; mx=max(d_up[1][u]-edges[i].w, tmp-edges[i].w); mx=max(mx, max(d_up[0][u]+tmp-edges[i].w-val[u],d_up[1][u]+tmp2-edges[i].w-val[u])); d_up[1][v]=val[v]+max(0, mx); dfs2(v,u); } } public static void main(String[] args) throws IOException { // TODO Auto-generated method stub Reader.init(System.in); t=Reader.nextInt(); head=new int[maxn+1]; for (int casenum=1;casenum<=t;casenum++) { n=Reader.nextInt(); val=new int[n+1]; cnt=0; for (int i=1;i<=n;i++) val[i]=Reader.nextInt(); for (int i=1;i<=n;i++) head[i]=-1; for (int i=1;i<n;i++) { u=Reader.nextInt(); v=Reader.nextInt(); c=Reader.nextInt(); cnt++; edges[cnt]=new Edge(v,c,head[u]); head[u]=cnt; cnt++; edges[cnt]=new Edge(u,c,head[v]); head[v]=cnt; } dfs1(1, 1); d_up[0][1]=d_up[1][1]=val[1]; dfs2(1,1); System.out.println("Case #"+casenum+":"); for (int i=1;i<=n;i++) System.out.println(max(d_up[0][i]+d_down[1][i], d_up[1][i]+d_down[0][i])-val[i]); } } }