AtCoder Grant Contest 010 C - Cleaning dfs+邏輯+dp思想

題意:給定一棵樹,每個節點的權值爲Ai,每次可以挑選兩個葉子節點,將兩個葉子節點之間路徑上的權值-1。問最後能不能使得整棵樹的權值爲0。


解法:驗證解的存在性,就去試着找出一組可行解。這題從最末端的情況開始想就可以很容易想出dp。

隨便找個非葉子節點,將其作爲整棵樹的根,現在對這個有根樹進行分析。

首先可以將路徑進行歸類,對於一個非葉子節點A,經過其的路徑可以分爲兩種情況:

1.經過一個子節點;

2.經過兩個子節點。

路徑經過兩個子節點意味着這條路不用往上走了,即不會影響A的父親。

路徑經過一個子節點意味着需要再向上傳遞,因爲另一個端點不在以A爲根的子樹裏。

接着構思dfs的細節:

1.考慮葉子節點,葉子節點的權值一定得向上傳遞,因爲葉子節點是路徑的端點。

2.考慮非葉子節點A‘,已知權值是Ai,設從其兒子節點需要傳上來的權值和是tot,其中第一種邊貢獻了x1,第二種邊貢獻了x2,其中,x1需要向上傳遞。容易列出方程:

x1+x2=Ai,2*x2+x1=tot,可以解出x1與x2的值。

接着要做的就是判斷x1與x2的合法性:

1.x1與x2均要大於等於0;

2.當x1與x2均大於等於0的時候,要能真正畫出這樣的情況。觀察發現,僅當兒子向上傳遞的權值分配很畸形的時候才畫不出來。設tm爲兒子中向上傳遞權值的最大值,當tm>Ai的時候,就不能畫出x1條第一種路徑,x2條第二種路徑。動手模擬一下就可以看出來。

最後,看一下整棵樹的根節點是不是x1等於0,若是的話,證明找到了可行解,因爲經過根節點的只能是第二種邊。

#include <cstdio>
#include <vector>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned int uii;
const int maxn=100005;
int n,a,b;
ll A[maxn];
vector<int> tr[maxn];
ll dfs(int u,int fa) {
    if (tr[u].size()==1)
        return A[u];
    ll tot=0,x2=0,x1=0,mm,tm=0;
    for (uii i=0;i<tr[u].size();++i) {
        if (tr[u][i]==fa)
            continue;
        mm=dfs(tr[u][i],u);
        tm=max(tm,mm);
        tot+=mm;
    }
    x2=tot-A[u];
    x1=A[u]-x2;
    if (x1<0||x2<0||A[u]<tm||(x1>0&&fa==-1)) {
        puts("NO");
        exit(0);
    }
    return x1;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
        scanf("%lld",&A[i]);
    if (n==1) {
        puts(A[1]?"NO":"YES");
        return 0;
    }
    for (int i=0;i<n-1;++i) {
        scanf("%d%d",&a,&b);
        tr[a].push_back(b);
        tr[b].push_back(a);
    }
    if (n==2)
        puts(A[1]==A[2]?"YES":"NO");
    else {
        for (int i=1;i<=n;++i)
            if (tr[i].size()>1) {
                dfs(i,-1);
                break;
            }
        puts("YES");
    }
    return 0;
}


發佈了69 篇原創文章 · 獲贊 45 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章