树形DP

 ① 相邻节点不能同时选时

  所得到权值最大:POJ2342 

 1 #include <iostream>
 2 #include <string.h>
 3 #include <cstdio>
 4 #include <math.h>
 5 #define SIGMA_SIZE 26
 6 #pragma warning ( disable : 4996 )
 7 
 8 using namespace std;
 9 typedef long long LL;
10 
11 inline int Max(int a,int b) { return a>b?a:b; }
12 inline int Min(int a,int b) { return a>b?b:a; }
13 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
14 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
15 const int inf = 0x3f3f3f3f;
16 const int maxn = 6005;
17 const int maxk = 2e6+5;
18 
19 
20 
21 int father[maxn], happy[maxn];
22 bool vis[maxn];
23 int dp[maxn][2];        //dp[i][0],dp[i][1]表示i不去或去产生的最大价值
24 int N, R;
25 
26 int find( int r )
27 {
28     vis[r] = 1;
29 
30 
31     for( int i = 1; i <= N; i++ )
32         if ( !vis[i] && father[i] == r )
33         {
34             find(i);
35             dp[r][1] += dp[i][0];
36             dp[r][0] += Max(dp[i][1],dp[i][0]);
37         }
38     return Max( dp[r][1], dp[r][0] );
39 } 
40 
41 void read()
42 {
43     memset( vis, 0, sizeof(vis) );
44     cin >> N;
45     int x, y;
46 
47     for ( int i = 1; i <= N; i++ )
48         scanf( "%d", &happy[i] );
49     for ( int i = 1; i < N; i++ )
50     {
51         scanf( "%d %d", &x, &y );
52         father[x] = y;
53         //son[y] = x;
54         R = y;
55     }
56     cin >> x >> y;
57 
58     for( int i = 1; i <= N; i++ )
59         dp[i][1] = happy[i];
60 }
61 
62 
63 int main()
64 {
65     read();
66     
67     cout << find(R) << endl;
68 
69     return 0;
70 }
View Code

  所得到权值最小:POJ1463,其实并不是最小..具体要求看题目

 1 #include <iostream>
 2 #include <string.h>
 3 #include <cstdio>
 4 #include <string>
 5 #include <math.h>
 6 #define SIGMA_SIZE 26
 7 #pragma warning ( disable : 4996 )
 8 
 9 using namespace std;
10 typedef long long LL;
11 
12 inline int Max(int a,int b) { return a>b?a:b; }
13 inline int Min(int a,int b) { return a>b?b:a; }
14 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
15 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
16 const int inf = 0x3f3f3f3f;
17 const int maxn = 2000;
18 const int maxk = 2e6+5;
19 
20 int dp[maxn][2], father[maxn];
21 int N, ans;
22 bool vis[maxn];
23 string road;
24 
25 int find( int r )
26 {
27     vis[r] = true;
28 
29     for ( int i = 0; i < N; i++ )
30         if ( !vis[i] && father[i] == r )
31         {
32             find(i);
33             dp[r][1] += Min( dp[i][0], dp[i][1] );
34             dp[r][0] += dp[i][1];
35         }
36     return Min( dp[r][0], dp[r][1] );
37 }
38 
39 void init()
40 {
41     ans = 0;
42     memset( dp, 0, sizeof(dp) );
43     memset( vis, 0, sizeof(vis) );
44     memset( father, -1, sizeof(father) );
45     
46     int u, v, num;
47     
48     for ( int j = 1; j <= N; j++ )
49     {
50         scanf( "%d:(%d)", &u, &num );
51         for ( ; num; num-- )
52         {
53             scanf("%d", &v);
54             father[v] = u;
55         }
56     }
57 
58 
59     for ( int i = 0; i < N; i++ )
60         dp[i][1] = 1;
61 }
62 
63 int main()
64 {
65     while ( ~scanf("%d", &N ) )
66     {
67         init();
68         
69         for ( int i = 0; i < N; i++ )
70             if ( !(father[i]+1) ) 
71                 ans += find(i);
72 
73         cout << ans << endl;
74     }
75     return 0;
76 }
View Code

   在基环树上取得的值最大: 基环树是指N个节点N条边的“树”,可见其上必然有环,但是删除环上任意一条边后可变成树,所以做法相比与上两题就是先求出环,把环所在的一条边删掉,再在树上做DP,这道题坑爹的是没说所有点是连通的,所以需要把答案加起来

  1 #include <iostream>
  2 #include <string.h>
  3 #include <cstdio>
  4 #include <queue>
  5 #include <math.h>
  6 #include <string>
  7 #include <map>
  8 #include <algorithm>
  9 #define SIGMA_SIZE 26
 10 #pragma warning ( disable : 4996 )
 11 
 12 using namespace std;
 13 typedef long long LL;
 14 
 15 inline int Max(int a,int b) { return a>b?a:b; }
 16 inline int Min(int a,int b) { return a>b?b:a; }
 17 inline LL LMax(LL a,LL b) { return a>b?a:b; }
 18 inline LL LMin(LL a,LL b) { return a>b?b:a; }
 19 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
 20 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
 21 const int inf = 0x3f3f3f3f;
 22 const int maxn = 1e6+5;
 23 const int maxk = 200;
 24 const int mod = 100000000;
 25 
 26 struct node {
 27     int next, to;
 28 }e[maxn<<1];
 29 
 30 int head[maxn], val[maxn];
 31 LL dp[2][maxn];
 32 bool vis[maxn];
 33 int cnt, N;
 34 int nopass, ringP1, ringP2;
 35 
 36 void addedge( int u, int v ) 
 37 {
 38     e[cnt].to = v; e[cnt].next = head[u]; head[u] = cnt++;
 39     e[cnt].to = u; e[cnt].next = head[v]; head[v] = cnt++;
 40 }
 41 
 42 void init()
 43 {
 44     cnt = 0;
 45     memset( head, -1, sizeof(head) );
 46     memset( vis, 0, sizeof(vis) );
 47 }
 48 
 49 void getDp( int t, int pre )
 50 {
 51     dp[0][t] = 0; dp[1][t] = val[t];
 52     
 53     for ( int i = head[t]; i+1; i = e[i].next )
 54     {
 55         if ( e[i].to == pre ) continue;
 56         if ( i == nopass || i == (nopass^1) ) continue;
 57 
 58         getDp( e[i].to, t );
 59         dp[0][t] += LMax( dp[1][e[i].to], dp[0][e[i].to] );
 60         dp[1][t] += dp[0][e[i].to];
 61     }
 62 }
 63 
 64 void dfs( int t, int pre ) 
 65 {
 66     vis[t] = true;
 67 
 68     for ( int i = head[t]; i+1; i = e[i].next )
 69     {
 70         int tmp = e[i].to;
 71 
 72         if ( tmp == pre ) continue;
 73         if ( !vis[tmp] ) dfs( tmp, t );
 74         else
 75         {    //不是父亲,又没被遍历过,说明是环的一部分
 76             nopass = i;
 77             ringP1 = tmp; ringP2 = t; 
 78         }
 79     }
 80 }
 81 
 82 int main()
 83 {
 84     init();
 85     cin >> N;
 86 
 87     int x;
 88     for ( int i = 1; i <= N; i++ )
 89         { scanf("%d %d", &val[i], &x ); addedge(x,i); }
 90 
 91     LL tmp, ans = 0;
 92 
 93     for ( int i = 1; i <= N; i++ )
 94     {
 95         if (vis[i]) continue;
 96 
 97         dfs(i, -1);
 98         getDp( ringP1, -1 );
 99         tmp = dp[0][ringP1];
100         
101         getDp( ringP2, -1 );
102         ans += LMax( tmp, dp[0][ringP2] );
103     }
104 
105     printf("%lld\n",ans);
106     return 0;
107 }
View Code

 

② 去掉一个点

  POJ2378:去掉一个点后剩下的连通分量中节点个数小于等于N/2,问这样的点有多少个(感觉并没有dp啊...完全就是dfs嘛)

 1 #include <iostream>
 2 #include <string.h>
 3 #include <cstdio>
 4 #include <queue>
 5 #include <string>
 6 #include <algorithm>
 7 #define SIGMA_SIZE 26
 8 #pragma warning ( disable : 4996 )
 9 
10 using namespace std;
11 typedef long long LL;
12 
13 inline int Max(int a,int b) { return a>b?a:b; }
14 inline int Min(int a,int b) { return a>b?b:a; }
15 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
16 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
17 const int inf = 0x3f3f3f3f;
18 const int maxn = 1e4+5;
19 const int maxk = 2e6+5;
20 
21 struct node {
22     int to, next;
23 }e[2*maxn];
24 
25 int N, cnt;
26 int sum[maxn], dp[maxn], linjie[maxn];
27 //dp[i]表示去掉i后,i的儿子形成的连通分量中,最多的节点个数
28 //sum[i]表示包括i及从i往下走的所有节点数
29 bool vis[maxn];
30 vector<int> ans;
31 
32 void addedges( int u, int v )
33 {
34     e[cnt].to = v; e[cnt].next = linjie[u]; linjie[u] = cnt++;
35     e[cnt].to = u; e[cnt].next = linjie[v]; linjie[v] = cnt++;
36 }
37 
38 int find( int r )
39 {
40     int tmp, v, mmax = 0;
41     sum[r]++;
42     vis[r] = true;
43     
44     for ( int i = linjie[r]; i+1; i = e[i].next )
45         if ( !vis[e[i].to] )
46         {
47             v = e[i].to;
48             tmp = find(v);
49 
50             mmax = Max(mmax, tmp);
51             sum[r] += tmp;
52         }
53     dp[r] = mmax;
54 
55     if ( r != 1 )
56         if ( dp[r] <= N/2 && N-sum[r] <= N/2 )
57             ans.push_back(r);
58 
59     return sum[r];
60 }
61 
62 void init()
63 {
64     cin >> N;
65     cnt = 0;
66     memset( vis, 0, sizeof(vis) );
67     memset( linjie, -1, sizeof(linjie) );
68 }
69 
70 int main()
71 {
72     init();
73 
74     int x, y;
75     for ( int i = 1; i < N; i++ )
76     {
77         scanf( "%d %d", &x, &y );
78         addedges(x,y);
79     }
80 
81     find(1);
82     if ( dp[1] <= N/2 )
83         ans.push_back(1);
84 
85     sort( ans.begin(), ans.end() );
86 
87     if ( ans.empty() )
88         cout << "NONE" << endl;
89     else
90         for ( vector<int>::iterator it = ans.begin(); it!=ans.end(); it++ )
91             cout << (*it) << endl;
92     return 0;
93 }
View Code

 

posted @ 2018-04-03 22:21  LBNOQYX  阅读(147)  评论(0编辑  收藏  举报