1 条题解

  • 0
    @ 2025-10-8 17:06:26
    #include <bits/stdc++.h>
    using namespace std;
    vector<int> G[110];
    int f[110][2][2];
    /*
    f[x][0][0]表示点x自己 不安全,没开, 
    f[x][0][1]表示点x自己 不安全, 开, 
    
    f[x][1][0]表示点x自己   安全,没开, 
    f[x][1][1]表示点x自己   安全,开, 
    
    这里的 "安全"    代表:以x为根的子树全部安全; 
           "不安全"  代表:以x为根的子树*只有*x点不安全。 
    */
    void dp(int x, int fa)
    {
        f[x][0][0] = 0;
        f[x][0][1] = 1;
        f[x][1][0] = 0;
        f[x][1][1] = 1;
    
        int c00 = 0, c01 = 0, c10 = 0, c11 = 0;
        int min00 = 110, min01 = 110, min10 = 110, min11 = 110; // minXX记录孩子节点改变状态的幅度
        bool bk = 0;
        for (int y : G[x]) if (y != fa)
        {
    
            dp(y, x);
            // f[x][0][0]表示点x自己 不安全,没开, 那么要求孩子节点有偶数个开了,即要求c00为偶数。 
            f[x][0][0] += min(f[y][1][0], f[y][1][1]);
            if (min(f[y][1][0], f[y][1][1]) == f[y][1][1]) c00++;
            min00 = min(min00, abs(f[y][1][0] - f[y][1][1]));
    
            // f[x][0][1]表示点x自己 不安全,  开, 那么要求孩子节点不安全,且有奇数个开了,即要求c01为奇数。
            f[x][0][1] += min(f[y][0][0], f[y][0][1]);
            if (min(f[y][0][0], f[y][0][1]) == f[y][0][1]) c01++;
            min01 = min(min01, abs(f[y][0][0] - f[y][0][1]));
    
            // f[x][1][0]表示点x自己   安全,没开, 那么要求孩子节点安全,且有奇数个开了,即要求c10为奇数。
            f[x][1][0] += min(f[y][1][0], f[y][1][1]);
            if (min(f[y][1][0], f[y][1][1]) == f[y][1][1]) c10++;
            min10 = min(min10, abs(f[y][1][0] - f[y][1][1]));
    
            // f[x][1][1]表示点x自己   安全,开, 那么要求孩子节点不安全,且有偶数个开了,即要求c11为偶数。
            f[x][1][1] += min(f[y][0][0], f[y][0][1]);
            if (min(f[y][0][0], f[y][0][1]) == f[y][0][1]) c11++;
            min11 = min(min11, abs(f[y][0][0] - f[y][0][1]));
            bk = 1;
        }
        if (bk == 1) // x为非叶子节点 
        {
            if (c00 % 2 == 1) f[x][0][0] += min00;
            /*此时希望c00是双数,代表着孩子结点开了双数次灯,相当于没有影响到x结点。
             如果c00是单数次,就会把x结点点亮,这时就要让代价最小的孩子结点改变状态。 */
            if (c01 % 2 == 0) f[x][0][1] += min01;
            /*此时希望c01是单数,把x结点点亮,f[x][0][1]的值不用再做改变。 
             如果c01是双数次,相当于没有影响到x结点,这时就要让代价最小的孩子结点改变状态。 */
            if (c10 % 2 == 0) f[x][1][0] += min10;
            /*此时希望c10是单数,把x结点点亮,f[x][1][0]的值不用再做改变。
             如果c10是双数次,相当于没有影响到x结点,这时就要让代价最小的孩子结点改变状态。 */
            if (c11 % 2 == 1) f[x][1][1] += min11;
            /*此时希望c11是双数,代表着孩子结点开了双数次灯,相当于没有影响到x结点。 
             如果c11是单数次,就会把x结点点亮,这时就要让代价最小的孩子结点改变状态 */
        }
        else     // x为  叶子节点 
        {
            f[x][0][0] = 0;
            f[x][0][1] = 110;
            f[x][1][0] = 110;
            f[x][1][1] = 1;
        }
    }
    int main()
    {
        int n;
        while (scanf("%d", &n) != EOF && n != 0)
        {
            memset(G, 0, sizeof(G));
            for (int i = 1, x, y; i < n; i++)
            {
                scanf("%d%d", &x, &y);
                G[x].emplace_back(y); G[y].emplace_back(x); // 双向边
            }
            int rt = 1; dp(rt, 0);
            printf("%d\n", min(f[rt][1][0], f[rt][1][1]));
        }
        return 0;
    }
    
    • 1

    *【树形DP】9:树[中山市选2009]

    信息

    ID
    4131
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    63
    已通过
    18
    上传者