#include <bits/stdc++.h>
using namespace std;
#define int long long

/*
sum<0なら0で終わり
そうじゃないなら？必ず正だけの地点を作る
根付き木にしてdp？
dp[i][j]:部分根付き木iまででj本切った時の最小値(iの上の部分を切ることも考えてる)
葉lに対してdp[l][0]=v[l],dp[l][1]=0;
親pに対して子をsとして、dp[p][k]=それぞれのそれ
つまり？dp[p][k]はmin(sum(dp[s][j]:sum j = k))
高速化の方法は？
えーなんかdpまでは絶対に合っていると思っていて、
+のやつを取るところに関しては+をなるべく大量に消費して欲しくて、マイナスは一個も入っちゃいけない
-が入る方は、+にならないようになるべく大きく取りたい
実は葉から貪欲？最後にくっつけられるところはくっつけていいとか
根付き木にして、葉から貪欲にやっていく　具体的には
・スタートが+の時
+で有る限り取り続ける
-が来た場合、-が大きくなるなら取って変更
・スタートが-の時
合計が負で有る限り取り続ける

貪欲が終わった時塊は　a:全部+で次の-入れても+ or b:合計が負で次のやつで+
・貪欲をした後
自分の子とくっつけられるかを試す？
自分がaの時　aは子の候補にいなくて、bは絶対無理　なので終わり
自分がbの時　子のbは全部取り込める
　　　　　　 これで子のaも取り込めるし次の+も取り込める場合は？
　　　　　　　・子のaを取り込むメリット:そのさらに子を取り込める可能性が出てくる
まってそもそも合流を考えられていない　いやそれは大丈夫かも
*/
int dp1[5123][5123],//dp1[v][k]:v以下k個、vのところは全部正の時のその最小値
    dp2[5123][5123],//dp2[v][k]:v以下k個、vの所の最小値
    dpt1[5123][5123],//dpt1[i][j]:vのi番目までの子までからなる部分木をdp1と同様にj個に分ける時の一番上の最小値
    dpt2[5123][5123];//dpt2[i][j]:同様

const int INF=1e18;

template<typename T>
void fin(T a){
  cout<<a<<endl;
  exit(0);
}

template<typename T>
void chmin(T &a,T b){
  if(a>b)a=b;
}
template<typename T>
void chmax(T &a,T b){
  if(a<b)a=b;
}

signed main(){
  int n;cin>>n;
  vector<int> v(n);
  for(int i=0;i<n;i++)cin>>v[i];
  vector<int> edge[n];
  for(int i=1;i<n;i++){
    int u,v;cin>>u>>v;u--;v--;
    edge[u].push_back(v);
    edge[v].push_back(u);
  }

  int sum=0;for(int i=0;i<n;i++)sum+=v[i];if(sum<0)fin(0);

  int r;int m=0;
  for(int i=0;i<n;i++)
    if(m<edge[i].size())
      m=edge[i].size(),r=i;

  vector<int> parent(n),son[n],d1(n,0),d2(n,0);
  parent[r]=-1;
  queue<int> que;
  que.push(r);
  while(que.size()){
    int p=que.front();que.pop();
    for(int q:edge[p]){
      if(parent[p]==q)continue;
      parent[q]=p;
      son[p].push_back(q);
      que.push(q);
    }
  }

  for(int i=0;i<5123;i++)
    for(int j=0;j<5123;j++)
      dp1[i][j]=dp2[i][j]=dpt1[i][j]=dpt2[i][j]=INF;

  vector<int> rem(n);
  for(int i=0;i<n;i++)rem[i]=son[i].size();
  for(int i=0;i<n;i++)if(!rem[i])que.push(i);

  while(que.size()){
    int p=que.front();que.pop();

    dpt1[0][0]=0;

    int N=son[p].size();

    int nowj=0,tmpj=0;
    for(int i=0;i<N;i++){
      int q=son[p][i];
      for(int j=0;j<=nowj;j++){
        if(dpt1[i][j]==INF)continue;
        for(int k=0;k<=d1[q];k++){
          if(dp1[q][k]==INF)continue;
          chmin(dpt1[i+1][j+k],dpt1[i][j]+dp1[q][k]);
          chmax(tmpj,j+k);
        }
        dpt1[i][j]=INF;
      }
      nowj=tmpj;
      tmpj=0;
    }

    for(int k=0;k<=nowj;k++){
      if(dpt1[N][k]==INF)continue;
      if(v[p]>0){
        chmin(dp1[p][k],dpt1[N][k]+v[p]);
        chmax(d1[p],k);
        chmin(dp2[p][k+1],0LL);
        chmax(d2[p],k+1);
      }
      dpt1[N][k]=INF;
    }

    dpt2[0][0]=0;

    nowj=tmpj=0;
    for(int i=0;i<N;i++){
      int q=son[p][i];
      for(int j=0;j<=nowj;j++){
        if(dpt2[i][j]==INF)continue;
        for(int k=0;k<=d2[q];k++){
          if(dp2[q][k]==INF)continue;
          chmin(dpt2[i+1][j+k],dpt2[i][j]+dp2[q][k]);
          chmax(tmpj,j+k);
        }
        dpt2[i][j]=INF;
      }
      nowj=tmpj;
      tmpj=0;
    }

    for(int k=0;k<=nowj;k++){
      if(dpt2[N][k]==INF)continue;
      chmin(dp2[p][k],dpt2[N][k]+v[p]);
      chmax(d2[p],k);
      if(dpt2[N][k]+v[p]<0){
        chmin(dp1[p][k+1],0LL);
        chmax(d1[p],k+1);
        chmin(dp2[p][k+1],0LL);
        chmax(d2[p],k+1);
      }
      dpt2[N][k]=INF;
    }
    if(~parent[p]&&--rem[parent[p]]==0)que.push(parent[p]);
  }

  int ans=INF;
  for(int j=0;j<5000;j++){
    if(dp1[r][j]!=INF)chmin(ans,j);
    if(dp2[r][j]<0)chmin(ans,j);
  }

  cout<<ans<<endl;
}


Measured memory_bytes: 821120