hdu5834 树型dp
赛场上队友都想出来了,居然弱得没调出来,,,
树型dp,dp1[u],dp2[u]分别代表从u开始(包括u)往子节点走回到u和不回到u所能取得的最大值
dp3[u],dp4[u]分别代表不包括自身向父节点走回来和不回来的最大值
两个dfs走一下就好,注意有很多细节
代码里面注释一下好了,,,毕竟调了那么久
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> #include <iostream> #include <map> #include <queue> #include <stack> //#pragma comment(linker, "/STACK:102400000,102400000") using namespace std; #define PF(x) cout << "debug: " << x << " "; #define EL cout << endl; #define PC(x) puts(x); typedef long long ll; #define CLR(x, v) sizeof (x, v, sizeof(x)) using namespace std; const int INF = 0x5f5f5f5f; const int N = 1000050; struct Edge { int to, cost; Edge(int to, int c) : to(to), cost(c) {} Edge() {} }; vector<Edge> G[N]; int dp1[N], dp2[N], //dp2回来 dp1不回来 dp3[N], dp4[N],ans[N][5],a[N]; // dp4回来 dp3不回来 void dfs(int u, int fa) { int cnt=G[u].size(); for (int i = 0; i < cnt; ++i) { int v = G[u][i].to; int c = G[u][i].cost; if (v == fa) continue; dfs(v, u); if (dp2[v]-c*2 > 0) dp2[u] += dp2[v]-c*2;//从这个子节点回来不亏就加上 } for(int i=0; i<cnt; i++)//遍历到底停在那个子树上最大值 { int v = G[u][i].to; int c = G[u][i].cost; if (v == fa) continue; if(dp2[v]-2*c > 0) { if( dp1[u]<dp2[u]+c-(dp2[v]-dp1[v])) { dp1[u]=dp2[u]+c-(dp2[v]-dp1[v]); ans[u][0]=v;//记录最大值 } } else if(dp1[v]>c) { if(dp1[u]<dp2[u]+dp1[v]-c) { dp1[u]=dp2[u]+dp1[v]-c; ans[u][0]=v; } } } int max1=a[u]; for(int i=0; i<cnt; i++)//遍历寻找次大值 { int v = G[u][i].to; int c = G[u][i].cost; if (v == fa) continue; if(dp2[v]-2*c > 0) { if( max1<dp2[u]+c-(dp2[v]-dp1[v])&&v!=ans[u][0]) { max1=dp2[u]+c-(dp2[v]-dp1[v]); ans[u][1]=i;//此处记录的是不定长数组中的序号 } } else if(dp1[v]>c) { if(max1<dp2[u]+dp1[v]-c&&v!=ans[u][0]) { max1=dp2[u]+dp1[v]-c; ans[u][1]=i; } } } } void dfs1(int u, int fa) { int cnt=G[u].size(); for (int i = 0; i < cnt; ++i) { int v = G[u][i].to; int c = G[u][i].cost; if (v == fa) continue; int num; if(dp2[v] > 2 * c)//求dp4[v]时只需要判断这个即可 { num=dp2[u]-dp2[v]+2*c; dp4[v]=max(dp4[v],dp4[u]+num-2*c); } else { dp4[v]=max(dp4[v],dp4[u]+dp2[u]-2*c); } if(v != ans[u][0])//当v并非dp1[u]停留的最大子树 { if(dp2[v] > 2*c) { dp3[v] = max(dp3[v], dp1[u] - (dp2[v] - 2*c) + dp4[u]-c);//v向父亲和兄弟中探索依然停留在ans[u][0] dp3[v] =max(dp3[v],dp2[u]-(dp2[v]-2*c)+dp3[u]-c);//停留在父亲及以上节点中,用上下两个max对两种情况比较 } else { dp3[v] = max(dp3[v], dp1[u]+dp4[u]-c); dp3[v] =max(dp3[v],dp2[u]+dp3[u]-c); } } else { if(ans[u][1]==-1)//当不存在次大点 { int max1=dp1[u]-(dp1[v]-c); dp3[v]=max(dp3[v],dp3[u]-c+max1); } else { int v1=G[u][ans[u][1]].to; int c1=G[u][ans[u][1]].cost; int max1=dp1[u]-(dp1[v]-c),max2;//max1代表当没有最大点那棵子树的值,max2代表在max1情况下停留在次大点的值 if(dp2[v1]>2*c1) max2=max1-(dp2[v1]-2*c1)+(dp1[v1]-c1); else max2=max1+dp1[v1]-c1; dp3[v]=max(dp3[v],max2+dp4[u]-c);//最终停留在次大点 dp3[v]=max(dp3[v],max1+dp3[u]-c);//最终停留在父节点或以上 } } dfs1(v,u); } } int main() { int T; int cas=0; // freopen("in.txt","r",stdin); cin>>T; while (T--) { cas++; int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d", a+i); ans[i][0]=-1; ans[i][1]=-1; } int u, v, c; for (int i = 1; i <=n; ++i) { G[i].clear(); dp1[i] = dp2[i] = a[i]; dp3[i] = dp4[i] = 0; } for (int i = 1; i < n; ++i) { scanf("%d%d%d", &u, &v, &c); G[u].push_back(Edge(v, c)); G[v].push_back(Edge(u, c)); } dfs(1, -1); dfs1(1,-1); printf("Case #%d:\n",cas); for(int i=1; i<=n; i++) { cout<<max(dp1[i]+dp4[i],dp2[i]+dp3[i])<<endl; } } return 0; }
下面是精简版,参考自
http://blog.csdn.net/fsss_7/article/details/52210266
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> #include <iostream> #include <map> #include <queue> #include <stack> //#pragma comment(linker, "/STACK:102400000,102400000") using namespace std; #define PF(x) cout << "debug: " << x << " "; #define EL cout << endl; #define PC(x) puts(x); typedef long long ll; #define CLR(x, v) sizeof (x, v, sizeof(x)) using namespace std; const int INF = 0x5f5f5f5f; const int N = 1000050; struct Edge { int to, cost; Edge(int to, int c) : to(to), cost(c) {} Edge() {} }; vector<Edge> G[N]; int dp1[N], dp2[N], //dp2回来 dp1不回来 dp3[N], dp4[N],ans[N][5],a[N]; // dp4回来 dp3不回来 void dfs(int u, int fa) { int cnt=G[u].size(); for (int i = 0; i < cnt; ++i) { int v = G[u][i].to; int c = G[u][i].cost; if (v == fa) continue; dfs(v, u); dp1[u]=max(dp1[u],dp1[u]+dp2[v]-2*c); if(dp2[u]+dp1[v]-c>dp1[u]) ans[u][0]=v; dp1[u]=max(dp1[u],dp2[u]+dp1[v]-c); dp2[u]=max(dp2[u],dp2[u]+dp2[v]-2*c); } } void dfs1(int u, int fa) { int cnt=G[u].size(); for (int i = 0; i < cnt; ++i) { int v = G[u][i].to; int c = G[u][i].cost; if (v == fa) continue; int num; if(dp2[v] > 2 * c)//求dp4[v]时只需要判断这个即可 { num=dp2[u]-dp2[v]+2*c; dp4[v]=max(dp4[v],dp4[u]+num-2*c); } else { dp4[v]=max(dp4[v],dp4[u]+dp2[u]-2*c); } if(v != ans[u][0])//当v并非dp1[u]停留的最大子树 { if(dp2[v] > 2*c) { dp3[v] = max(dp3[v], dp1[u] - (dp2[v] - 2*c) + dp4[u]-c);//v向父亲和兄弟中探索依然停留在ans[u][0] dp3[v] =max(dp3[v],dp2[u]-(dp2[v]-2*c)+dp3[u]-c);//停留在父亲及以上节点中,用上下两个max对两种情况比较 } else { dp3[v] = max(dp3[v], dp1[u]+dp4[u]-c); dp3[v] =max(dp3[v],dp2[u]+dp3[u]-c); } } else { int max1=0,num; num=dp1[v]-c; num=dp1[u]-num; for(int j=0;j<cnt;j++){ int v1 = G[u][j].to; int c1 = G[u][j].cost; if (v1 == fa) continue; if(v1==v) continue; if(dp2[v1]>2*c1){ max1=max(max1,num-(dp2[v1]-2*c1)+dp1[v1]-c1+dp4[u]-c); } else max1=max(max1,num+dp1[v1]-c1+dp4[u]-c); } max1=max(max1,num+dp3[u]-c); dp3[v]=max(dp3[v],max1); } dfs1(v,u); } } int main() { int T; int cas=0; // freopen("in.txt","r",stdin); cin>>T; while (T--) { cas++; int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d", a+i); ans[i][0]=-1; ans[i][1]=-1; } int u, v, c; for (int i = 1; i <=n; ++i) { G[i].clear(); dp1[i] = dp2[i] = a[i]; dp3[i] = dp4[i] = 0; } for (int i = 1; i < n; ++i) { scanf("%d%d%d", &u, &v, &c); G[u].push_back(Edge(v, c)); G[v].push_back(Edge(u, c)); } dfs(1, -1); dfs1(1,-1); printf("Case #%d:\n",cas); for(int i=1; i<=n; i++) { cout<<max(dp1[i]+dp4[i],dp2[i]+dp3[i])<<endl; } } return 0; }