树  bzoj-2466 中山市选-2009

题目大意:给定一棵树,每一个点有一个按钮和一个灯泡。如果按下一个点的按钮那么和这个点直接相连的点包括这个点的灯泡的状态会改变。如果是点亮就会变成熄灭,如果是熄灭就会变成点亮。

注释:$1\le n\le n$

想法:啥jb数据范围啊,不是树形dp吗?看着挺像高斯消元。看网上题解全是枚举自由元???O(n)就可以为什么要$O(n^3)$???

我们来看看树形dp...

状态:f[pos][bool]表示按下这个点的按钮之后,这个点亮(或不亮),它的所有子孙都亮的最小代价。g[pos][bool]表示不按这个点的按钮,这个点亮(不亮),它的所有子孙都亮的方案数。

转移

f[pos][1]=min(f[pos][0] + f[to[i]][0] , f[pos][1] + g[to[i]][0]).

f[pos][0]=min(f[pos][0] + g[to[i]][0] , f[pos][1] + f[to[i]][0]).

g[pos][1]=min(g[pos][0] + f[to[i]][1] , g[pos][1] + g[to[i]][1]).

g[pos][0]=min(g[pos][0] + g[to[i]][1] , g[pos][1] + f[to[i]][1]).

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 110 
using namespace std;
int to[N<<1],nxt[N<<1],head[N],tot;
int f[N][5],g[N][5],n;
inline void add(int x,int y)
{
	to[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void dfs(int pos,int fa)
{
	f[pos][1]=1,f[pos][0]=n<<1;
	g[pos][0]=0,g[pos][1]=n<<1;
	for(int i=head[pos];i;i=nxt[i])
	{
		if(to[i]==fa) continue;
		dfs(to[i],pos);
		int f1=f[pos][1],f0=f[pos][0],g1=g[pos][1],g0=g[pos][0];
		f[pos][1]=min(f0+f[to[i]][0],f1+g[to[i]][0]);
		f[pos][0]=min(f0+g[to[i]][0],f1+f[to[i]][0]);
		g[pos][1]=min(g0+f[to[i]][1],g1+g[to[i]][1]);
		g[pos][0]=min(g0+g[to[i]][1],g1+f[to[i]][1]);
	}
}
void original()
{
	tot=0;
	memset(head,0,sizeof head);
}
int main()
{
	while(~scanf("%d",&n))
	{
		if(!n) return 0;
		original();
		for(int x,y,i=1;i<=n-1;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
		dfs(1,0);
		printf("%d\n",min(f[1][1],g[1][1]));
	}
}

小结:觉得树形dp很强啊... ...