zoj 3527 带环的树形DP
很好的树形DP,破环为树;
这道题目很特殊,看到的时候根本无从下手,因为以前没接触过这种类型的树形DP,网上搜了一下发现自己简直弱爆了。。
这是一类最简单的树形DP,考虑某个节点选或不选,然后把两种状态从叶子向根更新上去即可,但是这道题中给你的不是一棵树,是图,特殊的图
每个点都只有一个父亲(或一个儿子),所以形成的环是简单环,所以可以考虑破环,从环上的一个点出发开始DP,这个点的两种状态(选或不选)分别枚举一下
View Code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long lld; const lld inf = (lld)1<<50; const int M = 100010; int w[M]; int n; int head[M],nxt[M],pnt[M],wi[M],p[M]; int E; lld dp[M][2]; void add(int a,int b,int w) { pnt[E]=b; wi[E]=w; nxt[E]=head[a]; head[a]=E++; } void dfs(int u,int F) { dp[u][0]=0; dp[u][1]=w[u]; for(int i=head[u];i!=-1;i=nxt[i]) { //printf("to=%d\n",pnt[i]); if(pnt[i]!=F) dfs(pnt[i],F); dp[u][0]+=max(dp[pnt[i]][0],dp[pnt[i]][1]); dp[u][1]+=max(dp[pnt[i]][0],dp[pnt[i]][1]+wi[i]); } } lld solve(int u) {//printf("u=%d\n",u); dp[u][0]=0; dp[u][1]=-inf;//不选u,这个状态不合法 lld ans1=0,ans2=0; for(int i=head[u];i!=-1;i=nxt[i]) { //printf("%d %d\n",pnt[i],u); dfs(pnt[i],u); ans1+=max(dp[pnt[i]][0],dp[pnt[i]][1]); } //printf("u=%d\n",u); dp[u][0]=-inf;//选u,这个状态不合法 dp[u][1]=w[u]; for(int i=head[u];i!=-1;i=nxt[i]) { //puts("bug"); dfs(pnt[i],u); ans2+=max(dp[pnt[i]][0],dp[pnt[i]][1]+wi[i]); } return max(ans1,ans2); } int vis[M]; int main() { while(scanf("%d",&n)!=EOF) { E=0; fill(head,head+n+1,-1); fill(p,p+n+1,-1); for(int i=1,j;i<=n;i++) { scanf("%d%d%d",&w[i],&j,&p[i]); add(p[i],i,j); } // printf("E=%d\n",E); fill(vis,vis+n+1,-1); lld ans=0; for(int i=1,j;i<=n;i++) { if(vis[i]!=-1) continue; for(j=i;vis[j]==-1;j=p[j]) vis[j]=i; // printf("%d %d\n",vis[j],i); if(vis[j]==i) ans+=solve(j); } printf("%lld\n",ans); } return 0; }