poj 1463 树形dp或者贪心(树的最小点覆盖)

题意:给定一棵树,选一个点集,使得所有边都与点集中的点相关联,求最小的点集。即最小点覆盖。

思路:一开始自己想到贪心,写出来也AC了,然后认识了树形dp。1、贪心:考虑叶子节点,如果选叶子节点,那么变换成不选叶子节点而选其父节点至少就能够覆盖这条边,而且还能覆盖更多的边。所以叶子节点是必然不选的,而其临接点是必然要选的。如此这般,用一个数组记录各点的度,用一个队列记录叶子节点,然后贪心选点。2、树形dp。一个点i选或不选只有两种可能,分别用dp[i][0]和dp[i][1]表示。dp[i][0]的话,其所有儿子必须选;dp[i][1]的话,其所有儿子可选可不选。用这个思路做dp即可。

1、贪心:

#include <stdio.h>
#include <string.h>
#define N 1505
struct edge{
	int x,y,next;
}e[N<<1];
int n,d[N],first[N],q[N],top;
void add(int x,int y){
	e[top].y = y;
	e[top].next = first[x];
	first[x] = top++;
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d",&n)!=EOF){
		int i,j,k,res=0,num,now,front,rear;
		top = 0;
		rear = front = -1;
		memset(first,-1,sizeof(first));
		memset(d,0,sizeof(d));
		for(k = 0;k<n;k++){
			scanf("%d:(%d)",&i,&num);
			while(num--){
				scanf("%d",&j);
				add(i,j);add(j,i);
				d[i]++;//记录端点的度数
				d[j]++;
			}
		}
		for(i = 0;i<n;i++)//将叶节点进队
			if(d[i] == 1)
				q[++rear] = i;
		while(front < rear){
			i = q[++front];
			if(!d[i])//如果与叶节点相连的边已经覆盖
				continue;
			for(j=first[i];j!=-1;j=e[j].next)//找叶节点的父节点
				if(d[e[j].y]){
					now = e[j].y;
					break;
				}
			d[now] = 0;//选定这个父节点
			res++;
			for(j=first[now];j!=-1;j=e[j].next){
				if(d[e[j].y] > 0)//注意:没有删边,所以对于端点度已经为0的就不再减了,因为前面有判断度是否为0的地方
					d[e[j].y]--;
				if(d[e[j].y] == 1)
					q[++rear] = e[j].y;
			}
		}
		printf("%d\n",res);
	}
	return 0;
}


2、树形dp:

#include <stdio.h>
#include <string.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define N 1505
struct edge{
	int x,y,next;
}e[N<<1];
int n,dp[N][2],first[N],top;
void add(int x,int y){
	e[top].y = y;
	e[top].next = first[x];
	first[x] = top++;
}
int dfs(int x,int f){//f为x节点的父亲
	int i,y;
	dp[x][1] = 1;//x点选择,则其至少为1
	for(i=first[x];i!=-1;i=e[i].next){
		y = e[i].y;
		if(y!=f){
			dfs(y,x);
			dp[x][0] += dp[y][1];
			dp[x][1] += min(dp[y][0],dp[y][1]);//一定注意儿子是可选可不选,而不是一定不选
		}
	}
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d",&n)!=EOF){
		int i,j,k,num;
		top = 0;
		memset(first,-1,sizeof(first));
		memset(dp,0,sizeof(dp));
		for(k = 0;k<n;k++){
			scanf("%d:(%d)",&i,&num);
			while(num--){
				scanf("%d",&j);
				add(i,j);add(j,i);
			}
		}
		dfs(0,0);
		printf("%d\n",min(dp[0][0],dp[0][1]));
	}
	return 0;
}


你可能感兴趣的:(poj 1463 树形dp或者贪心(树的最小点覆盖))