树形DP

1.poj 2342

  题意:邀请同事参加party,保证职员与直接上司不一起参加,每个人有个搞笑值,求邀请所有人的最大的搞笑值总和。
    DP部分:dp[0][i]表示职员i不来参加party,以i为根的子树的最大搞笑值,dp[1][i]表示职员i来参加party,以i为根的子树的最大搞笑值。

    所以DP状态转移方程为:
       dp[0][u] = 所有儿子v的(max(dp[0][v], dp[1][v]))之和;
       dp[1][u] = 所有儿子v的(dp[0][v])之和 + val[u];

View Code
 1 #include
 2 #include
 3 #include
 4 #include
 5 
 6 using namespace std;
 7 const int MAX=6005;
 8 
 9 struct tree
10 {
11     int v,next;
12 }edge[MAX];
13 
14 int k,val[MAX],head[MAX],le[MAX],rt[MAX],dp[2][MAX];
15 
16 void addedge(int u,int v)
17 {
18     edge[k].v=v;
19     edge[k].next=head[u];
20     head[u]=k++;
21 }
22 
23 void dfs(int u)
24 {
25     int v,i;
26     if(le[u])
27     {
28         dp[0][u]=0;
29         dp[1][u]=val[u];
30         return ;
31     }
32     for(i=head[u];i;i=edge[i].next)
33     {
34         v=edge[i].v;
35         dfs(v);
36         dp[0][u]+=max(dp[0][v],dp[1][v]);
37         dp[1][u]+=dp[0][v];
38     }
39     dp[1][u]+=val[u];
40 }
41 
42 int main()
43 {
44     int n,i,j,u,v;
45     while(scanf("%d",&n)!=EOF)
46     {
47         memset(head,0,sizeof(head));
48         memset(dp,0,sizeof(dp));
49         k=1;
50         for(i=1;i<=n;i++)
51         {
52             scanf("%d",&val[i]);
53             le[i]=rt[i]=1;
54         }
55         for(i=1;;i++)
56         {
57             scanf("%d%d",&v,&u);
58             if(v==0&&u==0)
59                 break;
60             addedge(u,v);
61             rt[v]=le[u]=0;
62         }
63         for(i=1;i<=n;i++)
64         {
65             if(rt[i])
66             {
67                 dfs(i);
68                 break;
69             }
70         }
71         printf("%d\n",max(dp[0][i],dp[1][i]));
72     }
73     return 0;
74 }

2.poj 3342

  详解见本站博文《状态压缩DP与树形DP》

View Code
  1 #include
  2 #include
  3 #include
  4 #include
  5 
  6 using namespace std;
  7 typedef long long ll;
  8 int k,w,le[201],rt[201],head[201],dp[2][201],way[2][201];
  9 char name[201][101];
 10 
 11 struct tree
 12 {
 13     int v,next;
 14 }edge[201];
 15 
 16 void addedge(int u,int v)
 17 {
 18     edge[k].v=v;
 19     edge[k].next=head[u];
 20     head[u]=k++;
 21 }
 22 
 23 int check(char* s)
 24 {
 25     int i;
 26     for(i=0;i)
 27     {
 28         if(strcmp(name[i],s)==0)
 29             return i;
 30     }
 31     strcpy(name[w++],s);
 32     return w-1;
 33 }
 34 
 35 void dfs(int u)
 36 {
 37     int i,v;
 38     if(le[u])
 39     {
 40         dp[0][u]=0;dp[1][u]=1;
 41         way[0][u]=way[1][u]=1;
 42         return ;
 43     }
 44     for(i=head[u];i!=-1;i=edge[i].next)
 45     {
 46         v=edge[i].v;
 47         dfs(v);
 48         dp[0][u]+=max(dp[0][v],dp[1][v]);
 49         dp[1][u]+=dp[0][v];
 50         if(way[0][v]==0)
 51             way[1][u]=0;
 52         if(dp[0][v]>dp[1][v]&&way[0][v]==0)
 53             way[0][u]=0;
 54         if(dp[0][v]1][v]&&way[1][v]==0)
 55             way[0][u]=0;
 56         if(dp[0][v]==dp[1][v])
 57             way[0][u]=0;
 58     }
 59     dp[1][u]++;
 60 
 61 }
 62 int main()
 63 {
 64     int n,i,u,v,flag;
 65     char c1[101],c2[101];
 66     ll ans;
 67     while(scanf("%d",&n)!=EOF&&n)
 68     {
 69         k=0;
 70         flag=0;
 71         memset(head,-1,sizeof(head));
 72         memset(dp,0,sizeof(dp));
 73         scanf("%s",name[0]);
 74         w=1;
 75         for(i=0;i)
 76         {
 77             way[0][i]=way[1][i]=1;
 78         }
 79         for(i=0;i)
 80             rt[i]=le[i]=1;
 81         for(i=1;i)
 82         {
 83             scanf("%s %s",c1,c2);
 84             v=check(c1);
 85             u=check(c2);
 86             addedge(u,v);
 87             le[u]=rt[v]=0;
 88         }
 89         dfs(0);
 90         if(dp[0][0]>dp[1][0])
 91         {
 92             ans=dp[0][0];
 93             if(way[0][0])
 94                 flag=1;
 95         }
 96         else
 97         {
 98             ans=dp[1][0];
 99             if(dp[0][0]==dp[1][0])
100             {
101                 if(way[0][0]+way[1][0]==1)
102                     flag=1;
103             }
104             else
105                 if(way[1][0])
106                     flag=1;
107         }
108         printf("%lld ",ans);
109         if(flag)
110             printf("Yes\n");
111         else
112             printf("No\n");
113     }
114     return 0;
115 }

 3.poj 1947

  题意:设定dp[i][j]为以i为根节点中有j个节点(包含i)的子树需要删除的边数。
   这里要用到背包,可以把p个节点的子树看作是容量,各个子树需要删除的边数为价值,要求得其价值最小。
   这道题的状态转移为dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]),
   但是要注意其初始化,以及dp[u][j]++;意思是每增加一个儿子,此时的dp[u][j]都需要多删一条边,
   然而之后却需要背包分配得出最小的dp[u][j]。

10654636    hankers    1947    Accepted    380K    0MS    C++    1404B    2012-08-11 10:50:49

View Code
 1 #include
 2 #include
 3 #include
 4 #include
 5 
 6 using namespace std;
 7 const int MAX=350;
 8 const int INF=100000;
 9 
10 struct tree
11 {
12     int v,next;
13 }edge[MAX];
14 int n,p,k,head[MAX],dp[MAX][MAX],visit[MAX];
15 
16 void addedge(int u,int v)
17 {
18     edge[k].v=v;
19     edge[k].next=head[u];
20     head[u]=k++;
21 }
22 
23 void dfs(int u)
24 {
25     int i,j,v,kk;
26     if(visit[u])
27         return ;
28     visit[u]=1;
29     dp[u][1]=0;
30     for(i=head[u];i;i=edge[i].next)
31     {
32         v=edge[i].v;
33         if(!visit[v])
34         {
35             dfs(v);
36             for(j=p;j>=1;j--)
37             {
38                 dp[u][j]++;
39                 for(kk=1;j-kk>0;kk++)
40                     dp[u][j]=min(dp[u][j],dp[u][j-kk]+dp[v][kk]);
41             }
42         }
43     }
44 }
45 
46 int main()
47 {
48     int u,v,i,j,ans;
49     while(scanf("%d%d",&n,&p)!=EOF)
50     {
51         memset(head,0,sizeof(head));
52         memset(visit,0,sizeof(visit));
53         k=1;
54         for(i=1;i<=n;i++)
55             for(j=1;j<=p;j++)
56                 dp[i][j]=INF;
57         for(i=1;i)
58         {
59             scanf("%d%d",&u,&v);
60             addedge(u,v);
61             addedge(v,u);
62         }
63         dfs(1);
64         ans=dp[1][p];
65         for(i=2;i<=n;i++)
66             if(ans>dp[i][p]+1)
67                 ans=dp[i][p]+1;
68         printf("%d\n",ans);
69     }
70     return 0;
71 }

4.hdu 1561

  题意:给出多组边(a,b)代表攻克a才可以攻克b,每个点都有权值,要求攻克M个点所得最大权值。
首先将其构造为一个树,0为根节点,即可以使用背包算法得出结果,其中注意的是初始化时,dp[u][1]=num[u];

6519139    2012-08-11 15:02:12    Accepted    1561    93MS    540K    1357 B    C++    hanker

View Code
 1 #include
 2 #include
 3 #include
 4 #include
 5 
 6 using namespace std;
 7 const int MAX=350;
 8 const int INF=1000000;
 9 
10 struct tree
11 {
12     int v,next;
13 }edge[MAX];
14 
15 int n,m,k,head[MAX],dp[MAX][MAX],num[MAX],visit[MAX];
16 
17 void addedge(int u,int v)
18 {
19     edge[k].v=v;
20     edge[k].next=head[u];
21     head[u]=k++;
22 }
23 
24 void dfs(int u)
25 {
26     int i,j,v,kk;
27     if(visit[u])
28         return ;
29     visit[u]=1;
30     dp[u][1]=num[u];
31     for(i=head[u];i!=-1;i=edge[i].next)
32     {
33         v=edge[i].v;
34         if(!visit[v])
35         {
36             dfs(v);
37             for(j=m+1;j>=1;j--)
38             {
39                 for(kk=1;kk)
40                         dp[u][j]=max(dp[u][j],dp[u][j-kk]+dp[v][kk]);
41             }
42         }
43     }
44 
45 }
46 
47 int main()
48 {
49     int u,v,i,j;
50     while(scanf("%d%d",&n,&m)!=EOF)
51     {
52         if(n==0&&m==0)
53             break;
54         memset(head,-1,sizeof(head));
55         memset(visit,0,sizeof(visit));
56         memset(num,0,sizeof(num));
57         k=0;
58         for(i=0;i<=n;i++)
59             for(j=1;j<=m+1;j++)
60                 dp[i][j]=-INF;
61         for(i=1;i<=n;i++)
62         {
63             scanf("%d%d",&u,&num[i]);
64             addedge(u,i);
65         }
66         dfs(0);
67         printf("%d\n",dp[0][m+1]);
68     }
69     return 0;
70 }

5.

待续...

 

你可能感兴趣的:(树形DP)