POJ3659 Cell Phone Network(树上最小支配集:树型DP)
题目求一棵树的最小支配数。
支配集,即把图的点分成两个集合,所有非支配集内的点都和支配集内的某一点相邻。
听说即使是二分图,最小支配集的求解也是还没多项式算法的。而树上求最小支配集树型DP就OK了。
树上的每个结点作为其子树的根可以有三个状态:
- 不属于支配集且还没被支配
- 不属于支配集但被其孩子支配
- 属于支配集
那么就是用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 }