http://acm.timus.ru/problem.aspx?space=1&num=1463
树形DP
此题有个陷阱 就是图的本身是一个森林 不是树
由于所有 happiness 的值都为非负 所有最优路径一定是从一个叶子节点到另一个叶子节点(当然 两个节点可能一样)
思路:
包含一个节点 和 两个节点的 树特殊处理
对应3个节点以及3个节点以上的树 一定能找到一个初度 大于等于2 的点 以此点为根节点更新
对于这个树 dfs 记录每个点向下的第一最优路径选择 和第二最优路径选择
然后不断向上更新
最后 把所有向下路径至少两条的节点 将此类节点的两个最优路径 连起来的路径进行和最终答案比较 择优而选
刚开始用 vector 建图 结果超内存了 看来 vector 的时间和空间占用都比较大
代码及其注释:
#include<iostream> #include<stdio.h> #include<string.h> #include<math.h> #include<algorithm> #include<vector> #include<set> #include<map> #include<string> #include <iomanip> using namespace std; const int N=50005; int head[N],I; struct node { int j,h,next; }side[N*2];//双向边 int out[N],sub[N],next1[N],next2[N];//初度 ,子树最优长度 ,第一选择路径,第二选择路径 vector<int>ans; int a[N]; void Add(int i,int j,int h) { side[I].j=j; side[I].h=h; side[I].next=head[i]; head[i]=I++; } int Optimal(int i)//以此点为根节点的树中 最优路径长度 { if(out[i]==0) return a[i]; if(out[i]==1) return a[i]+a[side[head[i]].j]+side[head[i]].h; return a[i]+side[next1[i]].h+sub[side[next1[i]].j]+side[next2[i]].h+sub[side[next2[i]].j]; } int Fhappy(int x,int i)//从x节点选择i路径向下的路径长度 { return (side[i].h+sub[side[i].j]); } int dfs(int x,int pre) { for(int t=head[x];t!=-1;t=side[t].next) { int l=side[t].j; if(l==pre) continue; dfs(l,x); if(next2[x]==-1||Fhappy(x,t)>Fhappy(x,next2[x]))//更新最优选择路径 next2[x]=t; else continue; if(next1[x]==-1||Fhappy(x,next2[x])>Fhappy(x,next1[x]))//更新最优选择路径 swap(next2[x],next1[x]); } sub[x]=a[x]; if(next1[x]!=-1) sub[x]+=Fhappy(x,next1[x]); return sub[x]; } int main() { //freopen("data.txt","r",stdin); int n,m; while(scanf("%d %d",&n,&m)!=EOF) { memset(head,-1,sizeof(head));I=0; memset(next1,-1,sizeof(next1)); memset(next2,-1,sizeof(next2)); memset(out,0,sizeof(out)); for(int i=1;i<=n;++i) scanf("%d",&a[i]); int l,r,h; while(m--) { scanf("%d %d %d",&l,&r,&h); Add(l,r,h); Add(r,l,h); ++out[l]; ++out[r]; } int k=-1; for(int i=1;i<=n;++i) { if(out[i]>=2&&next1[i]==-1) { dfs(i,-1); if(k==-1||Optimal(i)>Optimal(k)) k=i; } } for(int i=1;i<=n;++i) { if(out[i]>=3) { if(k==-1||Optimal(i)>Optimal(k)) k=i; }else if(out[i]==0)//只有一个点才情况 { if(k==-1||Optimal(i)>Optimal(k)) k=i; }else if(out[i]==1&&out[side[head[i]].j]==1)//只有两个点的情况 { if(k==-1||Optimal(i)>Optimal(k)) k=i; } } if(out[k]<=1) { printf("%d\n",Optimal(k)); if(out[k]==0) {printf("1\n");printf("%d\n",k);} else {printf("2\n");printf("%d %d\n",k,side[head[k]].j);} continue; } ans.clear(); ans.push_back(k);//将最优路径存到 ans 里面 int tmp=side[next1[k]].j; do{ ans.push_back(tmp); if(next1[tmp]==-1) break; tmp=side[next1[tmp]].j; }while(true); tmp=side[next2[k]].j; do{ ans.insert(ans.begin(),tmp); if(next1[tmp]==-1) break; tmp=side[next1[tmp]].j; }while(true); printf("%d\n",Optimal(k)); printf("%d\n",ans.size()); for(unsigned int i=0;i<ans.size();++i) printf("%d ",ans[i]); printf("\n"); } return 0; }