A - Anniversary party
这个题目是说,公司开宴会,为了让这个宴会的欢乐值尽量大,所以我们规定每一个员工的直属上司不能去,或者说每一个上司的直属员工不能去 , 然后求这个宴会的最大快乐值。
这个题目是我树形dp的入门题吧,虽然我到现在都还是不太懂树形dp,感觉就是模模糊糊的一个理解。
dp[i][0]:表示不邀请i员工其子树达到的最大快乐值,dp[i][1]则表示邀请。
其实我也不太懂为什么是这样定义的,不过暂时就这么想着吧。
这个需要建图,然后就是在树上进行dp。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <vector> #include <queue> #include <iostream> using namespace std; const int maxn = 6e3 + 10; vector<int>G[maxn]; int a[maxn]; int f[maxn]; int dp[maxn][2]; void dfs(int u) { for(int i=0;i<G[u].size();i++) { dfs(G[u][i]); } for(int i=0;i<G[u].size();i++) { dp[u][0] += max(dp[G[u][i]][0], dp[G[u][i]][1]); dp[u][1] += dp[G[u][i]][0]; } } int main() { int n; scanf("%d", &n); memset(f, -1, sizeof(f)); for (int i = 1; i <= n; i++) scanf("%d", &dp[i][1]); int so, fa; while(scanf("%d%d",&so,&fa)==2&&(so+fa)) { G[fa].push_back(so); f[so] = 1; } int root = 0; for(int i=1;i<=n;i++) { if (f[i] == -1) root = i; } dfs(root); printf("%d ", max(dp[root][0], dp[root][1])); return 0; }
B - Strategic game POJ - 1463
这个题目和上面那个是一个意思,可以一样的进行dp,状态转移方程只是微变。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstdlib> #include <queue> #include <vector> #include <algorithm> #include <iostream> #include <cstring> #define inf 0x3f3f3f3f using namespace std; const int maxn = 1e5 + 10; typedef long long ll; int dp[maxn][10]; vector<int>G[maxn]; bool vis[maxn]; void dfs(int u) { for(int i=0;i<G[u].size();i++) { dfs(G[u][i]); } for(int i=0;i<G[u].size();i++) { dp[u][0] += dp[G[u][i]][1]; dp[u][1] += min(dp[G[u][i]][0], dp[G[u][i]][1]); } } int main() { int n; while(scanf("%d",&n)!=EOF) { memset(dp, 0, sizeof(dp)); for (int i = 0; i <= n; i++) { G[i].clear(); vis[i] = 0; dp[i][1] = 1; } for(int i=1;i<=n;i++) { int u, v, cnt; scanf("%d:(%d)", &u, &cnt); for(int j=1;j<=cnt;j++) { scanf("%d", &v); G[u].push_back(v); vis[v] = 1; } } int root = 0; for (int i = 0; i < n; i++) if (vis[i] == 0) { root = i; break; } dfs(root); printf("%d ", min(dp[root][1], dp[root][0])); } return 0; }
C - Tree Cutting POJ - 2378
题目大意:给一颗n个结点的树,节点编号为1~n,问删除一个节点之后,让剩下的分支中节点数量最多的尽量少。可能有多种方案,按编号顺序输出。
这个题目说是一个树形dp,但是我感觉更像是一个搜索的思维题,
这个题目说是要删去一部分节点,使得之后形成的联通块的所有节点小于等于n/2
这个我们可以把任何一个点当作是根节点,所以我们只要求出这个根节点的子树的节点最大也要小于等于n/2就是满足条件的节点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstdlib> #include <cstring> #include <vector> #include <queue> #include <algorithm> #define inf 0x3f3f3f3f using namespace std; const int maxn = 1e4 + 10; int dp[maxn], num[maxn]; vector<int>G[maxn]; int n; int dfs1(int u,int pre) { int sum = 1; for(int i=0;i<G[u].size();i++) { if (G[u][i] == pre) continue; int res=dfs1(G[u][i], u); sum += res; num[u] = max(num[u], res); } num[u] = max(num[u], n - sum); return sum; } int main() { while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n-1;i++) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } dfs1(1, -1); for(int i=1;i<=n;i++) { if (num[i] <= n / 2) printf("%d ", i); } } return 0; }