POJ3659 Cell Phone Network(树上最小支配集:树型DP)

题目求一棵树的最小支配数。

支配集,即把图的点分成两个集合,所有非支配集内的点都和支配集内的某一点相邻。

听说即使是二分图,最小支配集的求解也是还没多项式算法的。而树上求最小支配集树型DP就OK了。

树上的每个结点作为其子树的根可以有三个状态:

  1. 不属于支配集且还没被支配
  2. 不属于支配集但被其孩子支配
  3. 属于支配集

那么就是用dp[u][1\2\3]来作为动归的状态,表示结点u为根子树的且u状态为1、2、3的最小支配数。

123转移该怎么转移就怎么转移。。最后的结果就是min(dp[root][2],dp[root][3])。

要注意的是对于有些结点前2个状态可能是不存在的,比如叶子结点不存在第2个状态、存在孩子是叶子结点的结点不存在第1个状态,这些不存在的状态要在转移的时候处理。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 #define INF 123456
 6 #define MAXN 111111
 7 struct Edge{
 8     int u,v,next;
 9 }edge[MAXN<<1];
10 int NE,head[MAXN];
11 void addEdge(int u,int v){
12     edge[NE].u=u; edge[NE].v=v; edge[NE].next=head[u];
13     head[u]=NE++;
14 }
15 int d[MAXN][3];
16 int dp(int u,int k,int fa){
17     if(d[u][k]!=-1) return d[u][k];
18     int res=0,diff=INF; bool flag=0,isLeaf=1;
19     for(int i=head[u]; i!=-1; i=edge[i].next){
20         int v=edge[i].v;
21         if(v==fa) continue;
22         isLeaf=0;
23         if(k==0){
24             if(dp(v,1,u)==INF) return d[u][k]=INF;
25             res+=dp(v,1,u);
26         }else if(k==1){
27             if(dp(v,2,u)<=dp(v,1,u)){
28                 res+=dp(v,2,u);
29                 flag=1;
30             }else{
31                 if(dp(v,1,u)==INF) return d[u][k]=INF;
32                 res+=dp(v,1,u);
33                 diff=min(diff,dp(v,2,u)-dp(v,1,u));
34             }
35         }else{
36             res+=min(min(dp(v,0,u),dp(v,1,u)),dp(v,2,u));
37         }
38     }
39     if(k==1 && isLeaf) return d[u][k]=INF;
40     if(k==1 && !flag) res+=diff;
41     return d[u][k]=res+(k==2);
42 }
43 int main(){
44     int n,a,b;
45     scanf("%d",&n);
46     NE=0;
47     memset(head,-1,sizeof(head));
48     for(int i=1; i<n; ++i){
49         scanf("%d%d",&a,&b);
50         addEdge(a,b); addEdge(b,a);
51     }
52     memset(d,-1,sizeof(head));
53     printf("%d",min(dp(1,1,0),dp(1,2,0)));
54     return 0;
55 }

 

你可能感兴趣的:(POJ3659 Cell Phone Network(树上最小支配集:树型DP))