通过模拟可以得到一个dp公式:
如果为0的话转移方程为:dp[0][u] += min(dp[1][v]+1,dp[0][v]),
如果为1的话转移方程为:dp[1][u] += min(dp[1][v],dp[0][v]+1),
如果为-1的话转移方程为:dp[0][u] += min(dp[1][v]+1,dp[0][v]);
dp[1][u] += min(dp[1][v],dp[0][v]+1);
最后的答案就是min(dp[1][1],dp[0][1]),建议自己模拟一下dp过程;
代码如下:
#include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<vector> using namespace std; const int mx = 1e5+5; const int inf = 1e6; int n,m; vector<int>g[mx]; int a[mx]; int dp[2][mx]; void dfs(int u,int fa) { if(a[u]==-1) dp[0][u] = dp[1][u] = 0; else dp[a[u]][u] = 0;/*将符合题意的初始状态定为0*/ for(int i=0;i<g[u].size();i++)/*把与该点连接的点扫一次*/ { int v=g[u][i]; if(v!=fa)/*要求不能是父节点*/ { dfs(v,u); if(a[u]==-1) { dp[0][u] += min(dp[0][v],dp[1][v]+1); dp[1][u] += min(dp[0][v]+1,dp[1][v]); } else dp[a[u]][u] += min(dp[a[u]][v],dp[a[u]^1][v]+1); } } } int main() { int t,q,ca = 1; scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i = 1; i <= n; i++) { scanf("%d",&a[i]); dp[0][i] = dp[1][i] = inf; g[i].clear(); } for(int i = 1; i < n; i++) { int u,v; scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u);/*将与该节点连接的点储存起来*/ } dfs(1,1);/*从根节点开始dp*/ printf("%d\n",min(dp[0][1],dp[1][1])); } return 0; }