树形dp专题
推荐一个博主写的——关于树形DP中求最小支配集,覆盖集,独立集的博客
他写的关于树形DP代码的解释很好懂
虽然还有求重心,树形背包什么的没讲,不过对于我这样刚入门的人来说应该是够了
最小支配集——POJ3659
题目含义
有1到N个点,给出点两两的相邻关系,这个图的最小支配集
题目分析
简单易懂的最小支配集问题,看着链接的博客很快就能写出来
但博客没讲一些细节问题
比如说遇到一个无向图可以用vis数组使无向变有向,这样dfs也只用一个参数就行了
还有根节点没有父节点,不能在求最小时加入dp[root][2]
题目代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; const int maxn=1e4+7; const int INF=0x3f3f3f3f; struct node{ int to; int next; }edge[maxn<<1]; int head[maxn],dp[maxn][3]; int n,a,b,tot; bool vis[maxn]; int min3(int a,int b,int c){ return min(a,min(b,c)); } void add(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } void dfs(int u){ vis[u]=true; dp[u][0]=1; dp[u][1]=dp[u][2]=0; int inc=INF; bool exist=false; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(!vis[v]){ dfs(v); dp[u][0]+=min3(dp[v][0],dp[v][1],dp[v][2]); dp[u][2]+=min(dp[v][0],dp[v][1]); if(dp[v][0]<=dp[v][1]){ exist=true; dp[u][1]+=dp[v][0]; } else{ inc=min(inc,dp[v][0]-dp[v][1]); dp[u][1]+=dp[v][1]; } } } if(!exist){ dp[u][1]+=inc; } } int main(){ scanf("%d",&n); memset(dp,0,sizeof(dp)); memset(head,-1,sizeof(head)); memset(vis,false,sizeof(vis)); tot=0; for(int i=1;i<n;i++){ scanf("%d%d",&a,&b); add(a,b); add(b,a); } dfs(a); printf("%d\n",min(dp[a][0],dp[a][1])); return 0; }
最小覆盖集——POJ1463
题目含义
给出N个点,以及每个点所相邻的点,求最小覆盖集
题目分析
与支配集相比只有两个状态,还不用讨论,简单多了
题目代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; const int maxn=1e4+7; const int INF=0x3f3f3f3f; struct node{ int to; int next; }edge[maxn<<1]; int head[maxn],dp[maxn][2]; int tot,t,n,m,a,root; char ch; void add(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } void dfs(int u){ dp[u][0]=0; dp[u][1]=1; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; dfs(v); dp[u][0]+=dp[v][1]; dp[u][1]+=min(dp[v][0],dp[v][1]); } } int main(){ while(~scanf("%d",&t)){ memset(dp,0,sizeof(dp)); memset(head,-1,sizeof(head)); tot=0; for(int i=0;i<t;i++){ scanf("%d%c%c%d%c",&n,&ch,&ch,&m,&ch); for(int j=0;j<m;j++){ scanf("%d",&a); add(n,a); } if(!i)root=n; } dfs(root); printf("%d\n",min(dp[root][0],dp[root][1])); } return 0; }
一遍过真是爽啊