1. 程式人生 > >BZOJ 1907 樹的路徑覆蓋【樹形DP】

BZOJ 1907 樹的路徑覆蓋【樹形DP】

好像可以貪心?

f [ i ] [ 1 / 0 ]

f[i][1/0] 表示選擇了/沒有選擇 i i 與父親間的邊時,以 i i 為根的子樹的最小路徑覆蓋數

就是平常轉移,算一下拐角:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define red(i,x,y) for(int i=(x);i>=(y);i--)
using namespace std;

const int N=2e4+5;

int t,n,f[N][2];
int cnt,to[N<<1],nxt[N<<1],head[N];

inline int read() {
	int x=0;char ch=getchar();bool f=0;
	while(ch>'9'||ch<'0'){if(ch=='-')f=1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return f?-x:x;
}

void ins(int x,int y) {
	to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;
}

void dfs(int x,int fa) {
	int ret=0;
	f[x][0]=f[x][1]=1;
	
	for(int i=head[x];i;i=nxt[i]) {
		int y=to[i];
		if(y!=fa) {
			dfs(y,x);
			f[x][0]=min(f[x][0]+f[y][0],f[x][1]+f[y][1]-1);
			f[x][1]=min(f[x][1]+f[y][0],ret+f[y][1]);ret+=f[y][0];
		}
	}
}

int main() {
	t=read();
	
	while(t--) {
		cnt=0;
		
		memset(f,0,sizeof(f));
		memset(head,0,sizeof(head)); 
		
		n=read();
		
		rep(i,2,n) {
			int x=read(),y=read();
			ins(x,y),ins(y,x);
		}
		
		dfs(1,0);
		
		printf("%d\n",min(f[1][0],f[1][1]));
	}

	return 0;
}