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

时间:2023-03-09 15:54:35
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个状态,这些不存在的状态要在转移的时候处理。

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 123456
#define MAXN 111111
struct Edge{
int u,v,next;
}edge[MAXN<<];
int NE,head[MAXN];
void addEdge(int u,int v){
edge[NE].u=u; edge[NE].v=v; edge[NE].next=head[u];
head[u]=NE++;
}
int d[MAXN][];
int dp(int u,int k,int fa){
if(d[u][k]!=-) return d[u][k];
int res=,diff=INF; bool flag=,isLeaf=;
for(int i=head[u]; i!=-; i=edge[i].next){
int v=edge[i].v;
if(v==fa) continue;
isLeaf=;
if(k==){
if(dp(v,,u)==INF) return d[u][k]=INF;
res+=dp(v,,u);
}else if(k==){
if(dp(v,,u)<=dp(v,,u)){
res+=dp(v,,u);
flag=;
}else{
if(dp(v,,u)==INF) return d[u][k]=INF;
res+=dp(v,,u);
diff=min(diff,dp(v,,u)-dp(v,,u));
}
}else{
res+=min(min(dp(v,,u),dp(v,,u)),dp(v,,u));
}
}
if(k== && isLeaf) return d[u][k]=INF;
if(k== && !flag) res+=diff;
return d[u][k]=res+(k==);
}
int main(){
int n,a,b;
scanf("%d",&n);
NE=;
memset(head,-,sizeof(head));
for(int i=; i<n; ++i){
scanf("%d%d",&a,&b);
addEdge(a,b); addEdge(b,a);
}
memset(d,-,sizeof(head));
printf("%d",min(dp(,,),dp(,,)));
return ;
}