poj3659 Cell Phone Network(树形dp)

来源:互联网 发布:毁灭战士4优化 编辑:程序博客网 时间:2024/05/18 03:16

题目链接:poj3659


题意:

给你一棵无向树,问你最少用多少个点可以覆盖掉所有其他的点。
(一个点被盖,它自己和与它相邻的点都算被覆盖

思路:

第一步:确定状态
dp[i][0]:选点i并且以点i为根的子树都被覆盖了。
dp[i][1]选点ii被其儿子覆盖
dp[i][2]选点ii没有被子节点覆盖(被其父亲覆盖)
(这里其实指的是此点的父亲一定要覆盖他  他对孩子的选择可以是dp[to][0],dp[to][1]中的较小值)
第二步:确定状态转移方程
dp[i][0]=1+Σmin(dp[u][0],dp[u][1],dp[u][2])(u是i的儿子
dp[i][2]=Σmin(dp[u][0],dp[u][1]);
对于dp[i][1]的讨论稍微复杂一点——他的所有儿子里面必须有一个取dp[u][1]
那么:if(i没有子节点)dp[i][1]=INF
else dp[i][1]=Σmin(dp[u][0],dp[u][1])+inc
其中对于inc有:
if(上面式子中的Σmin(dp[u][0],dp[u][1])中包含某个dp[u][0])inc=0;
else inc=min(dp[u][0]-dp[u][1])

这里需要注意的是最后的输出:printf("%d\n",min(dp[1][0],min(dp[1][1],dp[1][2]+1)));
#include<iostream>#include<iostream>#include<cstring>#include<string.h>#include<algorithm>#include<cstdio>#include<cmath>using namespace std;#define maxn 10100#define inf 0x3f3f3f3fint n,m;int dp[maxn][3];/*①dp[i][0]:选点i,并且以点i为根的子树都被覆盖了。②dp[i][1]:不选点i,i被其儿子覆盖③dp[i][2]:不选点i,i没有被子节点覆盖(被其父亲覆盖*/struct node{int to,next;}edge[maxn*2];int head[maxn];bool vis[maxn];int cnt;int min(int a,int b){    return a<b?a:b;}void dfs(int u){vis[u]=1;dp[u][0]=1;dp[u][1]=0;dp[u][2]=0;int flag=0;int mine=inf;for(int i=head[u];i!=-1;i=edge[i].next){int to=edge[i].to;if(vis[to])continue;dfs(to);dp[u][0]+=min(dp[to][0],min(dp[to][1],dp[to][2]));        dp[u][2]+=min(dp[to][0],dp[to][1]);if(dp[to][0]<=dp[to][1]){dp[u][1]+=dp[to][0];flag=1;}else{dp[u][1]+=dp[to][1];mine=min(mine,dp[to][0]-dp[to][1]);}}if(!flag){dp[u][1]+=mine;}}int main(){while(~scanf("%d",&n)){memset(head,-1,sizeof head);memset(dp,0,sizeof dp);memset(vis,0,sizeof vis);int a,b,c;cnt=0;for(int i=1;i<n;i++){scanf("%d%d",&a,&b);edge[cnt].to=b;edge[cnt].next=head[a];head[a]=cnt;cnt++;edge[cnt].to=a;edge[cnt].next=head[b];head[b]=cnt;cnt++;}dfs(1);printf("%d\n",min(dp[1][0],min(dp[1][1],dp[1][2]+1)));}return 0;}