树状dp的实质,我想在我的上一篇博客里总结的挺好的(不知道可以看上一篇qwq)。
能自己写出来一篇代码并且AC,感觉真不错……
其实我觉得都不用怎么解释了……
给出定义:f [ x ] [ y ] 表示把节点 x 染色为 y ,在以 x 为根的子树里最少需要染的点的数量。
注意一下转移方程,不明白的自己画个图推一推,推不出来的再看看定义,在这里就不解释了。(实在不明白一号机位欢迎你)
这道题我第三次才过……
第一次RE了,因为点的数量和边的数量是不一样的,一定要注意。
第二次是TLE,因为滴bug的时候,只删掉了输出部分,上面的循环忘删了(emmm),所以我的dfs跑了 m - 1 遍……不T才怪呢qwq。
又想起了ssy曾经和我说的话,题解可以看,但是绝不能抄,那就是欺骗自己。
最后附上代码:
#include<cstdio> #include<iostream> using namespace std; #define maxn 20010 #define inf 1e9 int head[maxn],nxt[maxn],to[maxn],out[maxn],f[maxn][3],c[5030]; int m,n,cnt; void add(int a,int b) { to[++cnt]=b; nxt[cnt]=head[a]; head[a]=cnt; } void dfs(int v,int fa) { if(out[v]==1) { f[v][c[v]]=1; f[v][c[v]^1]=inf; return ; } f[v][1]=f[v][0]=1; for(int i=head[v];i;i=nxt[i]) { int u=to[i]; if(u==fa) continue; dfs(u,v); f[v][0]+=min(f[u][1],f[u][0]-1); f[v][1]+=min(f[u][0],f[u][1]-1); } } void Deal_first() { scanf("%d%d",&m,&n); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=m-1;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); out[x]++; out[y]++; } } int main() { Deal_first(); dfs(n+1,0); printf("%d",min(f[n+1][1],f[n+1][0])); return 0; }