POJ 3659 Cell Phone Network【最小支配集】
题意:有以个 有 N 个节点的树形地图,问在这些顶点上最少建多少个电话杆,可以使得所有顶点被覆盖到,一个节点如果建立了电话杆,那么和它直接相连的顶点也会被覆盖到。
分析:用最少的点覆盖所有的点,即为求最少支配集。 可以用树形DP。
① dp[r][0] += min(dp[i][0],dp[i][1],dp[i][2]) dp[r][0]表示在自 r 顶点自身建, 以 r 为根节点的树所需要的最少覆盖数。
② dp[r][1] += min(dp[i][0],dp[i][1]) dp[r][1]表示在r 的子节点建, 以 r 为根节点的树所需要的最少覆盖数。
③ dp[r][2] += min(dp[i][0],dp[i][1]) dp[r][2]表示在r 的父节点建, 以 r 为根节点的数所需要的最少覆盖数。
其中第 ② 个有 特殊情况需要考虑,即如果每次 min () 取得都是dp[i][1]的话,即 r 所有的子节点的都没有建电话杆的话,这样得出的结果就会有误差,可以在其中记录最小
的dp[i][0] 值和其对应的 dp[i][1] 值,最后在这个 i 上建电话杆,就能保证自己被覆盖到,且用的电话杆最少, 这时dp[r][1]+=dp[i][0]-dp[i][1] 即可。
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) int min(int a,int b) { return a<b?a:b; } #define maxn 100005 #define INF 0x1f1f1f1f struct node { int to; int next; }e[1000000]; int tot; int head[maxn]; int dp[maxn][3]; void add(int s,int u) { e[tot].to=u; e[tot].next=head[s]; head[s]=tot++; } // dp[r][0] = min(dp[i][0],dp[i][1],dp[i][2])自身建 // dp[r][1] = min(dp[i][0],dp[i][1]) 子节点建 // dp[r][2] = min(dp[i][0],dp[i][1]) 父节点建 int n; int v[maxn]; void dfs(int r) { v[r]=1; int i,k; int flag=1,ff=1,mi=INF,t; dp[r][0]=1; for(i=head[r];i;i=e[i].next) { k=e[i].to; if(!v[k]) { flag=0; dfs(k); dp[r][0]+=min(dp[k][0],min(dp[k][1],dp[k][2])); if(dp[k][1]>=dp[k][0]) { ff=0; dp[r][1]+=dp[k][0]; } else { dp[r][1]+=dp[k][1]; if(dp[k][0]<mi) { mi=dp[k][0]; t=dp[k][1]; } } dp[r][2]+=min(dp[k][0],dp[k][1]); } } if(ff) dp[r][1]+=mi-t; if(flag) { dp[r][0]=1; dp[r][1]=INF; dp[r][2]=0; } } int main() { int a,b,i; while(scanf("%d",&n)!=EOF) { tot=1; clr(v); clr(dp); clr(head); for(i=1;i<n;i++) { scanf("%d%d",&a,&b); add(a,b); add(b,a); } if(n==1) { printf("1\n"); continue; } dfs(1); printf("%d\n",min(dp[1][0],dp[1][1])); } return 0; }