① 相邻节点不能同时选时
所得到权值最大:POJ2342
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
所得到权值最小:POJ1463,其实并不是最小..具体要求看题目
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
在基环树上取得的值最大: 基环树是指N个节点N条边的“树”,可见其上必然有环,但是删除环上任意一条边后可变成树,所以做法相比与上两题就是先求出环,把环所在的一条边删掉,再在树上做DP,这道题坑爹的是没说所有点是连通的,所以需要把答案加起来
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 ",ans); 106 return 0; 107 }
② 去掉一个点
POJ2378:去掉一个点后剩下的连通分量中节点个数小于等于N/2,问这样的点有多少个(感觉并没有dp啊...完全就是dfs嘛)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }