题意:一棵树,进行染色,每个没染色的节点恰好和一个染色的节点相连,求染色的节点最少的个数X(以下均以X代表子问题的解)
思路:树形DP,细化状态,从而对每个节点的每种状态互相递推
这里如何细化状态是难点,而且也是这类难题的共同问题
很容易知道每个节点i至少两个状态:dp[i][0]: i没染上色时以i的子树的X。dp[i][1]: i被染色以i为子树的X
但是仅仅这两个状态无法实现状态转移因为:
dp[u][0]=sum(dp[v][0],dp[v][1])+1 (dp[u][0]可以找到状态转移关系,dp[u][0]和u的父亲没关系)
但是dp[u][1]不仅仅与u的儿子有关系,也和u的父亲有关系,比如当他的父亲染了色,u的儿子必须全没染色,当u的父亲没染色,那么u的儿子必须有一个染了色
所以再添加一个状态来表示父亲的情况:dp[u][2],表示u和u的父亲都没染色,dp[u][1]改成u没染色但u的父亲染了色
这样递推关系就可以写出来了。
所以这类树形dp,要学会适当的添加状态,如何细化状态
// Accepted C++ 0.016 2015-03-14 12:41:27 #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int N= 1e4+1e2; const int inf = 0x3f3f3f3f; int n,op; struct Edge { int v; Edge(int _v=0) : v(_v) {}; }; int dep[N]; //每个节点的深度 int fa[N]; //每个节点的父亲节点 int mdep; int dp[N][3]; //每个节点为根的子树三个状态互相递推 vector<Edge> es[N]; void dfs(int u,int pa) { fa[u]=pa; if(u==1) dep[u]=0; else dep[u]=dep[pa]+1; mdep=max(mdep,dep[u]); for(int i=0;i<es[u].size();i++) { int v=es[u][i].v; if(v==pa) continue; dfs(v,u); } } void ini() { for(int i=1;i<=n;i++) es[i].clear(); mdep=-1; memset(dp,0,sizeof(dp)); } void debug() { printf("mdep=%d\n",mdep); printf("\n0:"); for(int i=1;i<=n;i++) printf("%11d",dp[i][0]); printf("\n1:"); for(int i=1;i<=n;i++) printf("%11d",dp[i][1]); printf("\n2:"); for(int i=1;i<=n;i++) printf("%11d",dp[i][2]); puts(""); } int main() { while(scanf("%d",&n),~op) { ini(); int u,v; for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); es[u].push_back(Edge(v)); es[v].push_back(Edge(u)); } scanf("%d",&op); dfs(1,0); for(int l=mdep;l>=0;l--) for(int u=1;u<=n;u++) if(dep[u]==l) { int sum1=0,sum2=0; int sz=es[u].size(); for(int j=0;j<sz;j++) { int v=es[u][j].v; if(v==fa[u]) continue; sum1+=min(dp[v][0],dp[v][1]); if(sum1>inf) sum1=inf; //防止爆int sum2+=dp[v][2]; if(sum2>inf ) sum2=inf; } dp[u][0]=sum1+1; dp[u][1]=sum2; for(int j=0;j<sz;j++) { int v=es[u][j].v; if(v==fa[u]) continue; else { dp[u][2]=inf; dp[u][2]=min(dp[u][2],dp[u][1]-dp[v][2]+dp[v][0]); } } if(sz==1&&u!=1) dp[u][2]=inf; //叶节点的一个边界,所以要判根 } printf("%d\n",min(dp[1][0],dp[1][2])); // debug(); } return 0; }
时间: 2024-10-13 02:44:50