树型DP维护从有根树的顶点出发到叶子的最长距离和次长距离(要保证通过不同的孩子)
POJ 1985
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int head[40010],cnt; int dp1[40010],dp2[40010]; int ans=0; struct Edge{ int v,c,next; }edge[80010]; void addedge(int u,int v,int c){ edge[cnt].v=v; edge[cnt].c=c; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].c=c; edge[cnt].next=head[v]; head[v]=cnt++; } void init(){ cnt=0; memset(head,-1,sizeof(head)); memset(dp1,0,sizeof(dp1)); memset(dp2,0,sizeof(dp2)); ans=0; } void dfs(int s,int father){ int i; bool flag=1; for(i=head[s];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v!=father){ dfs(v,s); flag=0; } } if(flag){ dp1[s]=dp2[s]=0; return ; } for(i=head[s];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v!=father){ if(dp1[v]+edge[i].c>dp1[s]){ dp2[s]=dp1[s]; dp1[s]=dp1[v]+edge[i].c; } else if(dp1[v]+edge[i].c>dp2[s]) dp2[s]=dp1[v]+edge[i].c; ans=max(ans,dp1[s]+dp2[s]); } } } int main(){ int n,m,i,j,k; int u,v,c; char dir; scanf("%d %d",&n,&m); init(); for(i=1;i<=m;i++){ scanf("%d %d %d %c",&u,&v,&c,&dir); addedge(u,v,c); } dfs(1,0); printf("%d\n",ans); return 0; }
BOJ 196
此题求两个叶子节点的最小距离
树形DP前特判n==2的情况
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int head[10010],cnt; int dp1[10010],dp2[10010]; int ans=0; int d[10010]; struct Edge{ int v,c,next; }edge[20010]; void addedge(int u,int v,int c){ edge[cnt].v=v; edge[cnt].c=c; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].c=c; edge[cnt].next=head[v]; head[v]=cnt++; } void init(int n){ cnt=0; memset(d,0,sizeof(d)); memset(head,-1,sizeof(head)); for(int i=1;i<=n;i++) dp1[i]=dp2[i]=60000000; ans=60000000; } void dfs(int s,int father){ int i; bool flag=1; for(i=head[s];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v!=father){ dfs(v,s); flag=0; } } if(flag){ dp1[s]=dp2[s]=0; return ; } for(i=head[s];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v!=father){ if(dp1[v]+edge[i].c<dp1[s]){ dp2[s]=dp1[s]; dp1[s]=dp1[v]+edge[i].c; } else if(dp1[v]+edge[i].c<dp2[s]) dp2[s]=dp1[v]+edge[i].c; ans=min(ans,dp1[s]+dp2[s]); } } } int main(){ int n,i; int u,v,c; while(scanf("%d",&n)==1){ if(n==0)break; init(n); for(i=1;i<n;i++){ scanf("%d %d %d",&u,&v,&c); addedge(u,v,c); d[u]++,d[v]++; } if(n==2){ printf("%d\n",c); continue; } for(i=1;i<=n;i++) if(d[i]>1){ dfs(i,0); break; } printf("%d\n",ans); } return 0; }
HDU 2196
分两次树形DP,第一次记录的有根树顶点向下所能到达的最长距离和次长距离,并保存最长距离经过的第一个儿子节点。
第二次记录有根树的顶点向上所能达到的最大距离
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int head[10010],cnt; int dp1[10010],dp2[10010],whe[10010]; struct Edge{ int v,c,next; }edge[20010]; void addedge(int u,int v,int c){ edge[cnt].v=v; edge[cnt].c=c; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].c=c; edge[cnt].next=head[v]; head[v]=cnt++; } void init(int n){ cnt=0; memset(head,-1,sizeof(head)); memset(dp1,0,sizeof(dp1)); memset(dp2,0,sizeof(dp2)); } void dfs1(int s,int father){ int i; bool flag=1; for(i=head[s];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v!=father){ dfs1(v,s); flag=0; } } if(flag){ dp1[s]=dp2[s]=0; return ; } for(i=head[s];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v!=father){ if(dp1[v]+edge[i].c>dp1[s]){ dp2[s]=dp1[s]; dp1[s]=dp1[v]+edge[i].c; whe[s]=v; } else if(dp1[v]+edge[i].c>dp2[s]) dp2[s]=dp1[v]+edge[i].c; } } } void dfs2(int s,int father){ int i; for(i=head[s];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v!=father){ if(whe[s]!=v){ if(dp1[s]+edge[i].c>dp1[v]){ dp2[v]=dp1[v]; dp1[v]=dp1[s]+edge[i].c; whe[v]=s; } else if(dp1[s]+edge[i].c>dp2[v]) dp2[v]=dp1[s]+edge[i].c; } else{ if(dp2[s]+edge[i].c>dp1[v]){ dp2[v]=dp1[v]; dp1[v]=dp2[s]+edge[i].c; whe[v]=s; } else if(dp2[s]+edge[i].c>dp2[v]) dp2[v]=dp2[s]+edge[i].c; } dfs2(v,s); } } } int main(){ int n,i; int v,c; while(scanf("%d",&n)==1){ init(n); for(i=2;i<=n;i++){ scanf("%d %d",&v,&c); addedge(i,v,c); } dfs1(1,0); dfs2(1,0); for(i=1;i<=n;i++) printf("%d\n",dp1[i]); } }