1. 程式人生 > >zoj-4007(樹形dp)

zoj-4007(樹形dp)

通過模擬可以得到一個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; }