zoukankan      html  css  js  c++  java
  • POJ3342 Party at HaliBula(树形DP)

    题目链接

    题意:

    给定一个树,选择若干点,使得选择的结点中任一结点不会和它的子结点同时选择,求能选结点最大数量。同时判断方案数是否为一。

    分析:

    如果单单求最大数量,很容易。之前也做过一个。链接:http://www.cnblogs.com/tanhehe/archive/2013/06/12/3132521.html

    但是如何判断方案数是否唯一呢?

    新加一个状态 dup[i][j],表示相应 dp[i][j] 是否唯一方案。

    对于叶子结点,dup[k][0] = dup[k][1] = 1.

    对于非叶子结点,

      1.对于 i 的任意儿子 j, 若(dp[j][0] > dp[j][1] 且 dup[j][0] == 0) 或 (dp[j][0] < dp[j][1] 且 dup[j][1] == 0) 或 (dp[j][0] == dp[j][1]), 则 dup[i][0] = 0.

      2.对于 i 的r任意儿子 j 有 dup[j][0] = 0, 则 dup[i][1] = 0。

    在这里,左儿子右兄弟的写法。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <map>
    
    using namespace std;
    
    const int maxn = 200 + 10;
    
    bool dup[maxn][2];
    
    struct Tree {
        int child, brother, father;
        int Take, Not;
    
        void init() {
            child = brother = father = Not = 0;
            Take = 1;
        }
    }tree[maxn];
    
    void dfs(int idx) {
        int child = tree[idx].child;
    
        while(child) {
            dfs(child);
    
            tree[idx].Take += tree[child].Not;
            tree[idx].Not += max(tree[child].Take, tree[child].Not);
    
            if((tree[child].Take > tree[child].Not && dup[child][1] == false) ||
               (tree[child].Take < tree[child].Not && dup[child][0] == false) ||
               (tree[child].Take == tree[child].Not))
                dup[idx][0] = false;
    
            if(dup[child][0] == false) dup[idx][1] = false;
    
            child = tree[child].brother;
        }
    }
    
    int main() {
        int n;
        char s1[100+10], s2[100+10];
    
        while(scanf("%d", &n) == 1 && n) {
            int cnt = 0;
            map<string, int> a;
    
            for(int i=1; i<=n; i++) {
                tree[i].init();
            }
    
            scanf("%s", s1);
            a[s1] = ++cnt;
    
            for(int i=2; i<=n; i++) {
                scanf("%s%s", s1, s2);
    
                if(a.count(s1) == 0) a[s1] = ++cnt;
                int d1 = a[s1];
                if(a.count(s2) == 0) a[s2] = ++cnt;
                int d2 = a[s2];
    
                tree[d1].father = d2;
                tree[d1].brother = tree[d2].child;
                tree[d2].child = d1;
            }
    
            memset(dup, true, sizeof(dup));
    
            dfs(1);
    
            printf("%d ", max(tree[1].Take, tree[1].Not));
    
            if((tree[1].Take > tree[1].Not && dup[1][1] == false)   ||
               (tree[1].Take < tree[1].Not && dup[1][0] == false)   ||
               tree[1].Take == tree[1].Not)
               printf("No\n");
            else printf("Yes\n");
        }
    }
    View Code

    或者直接建树,写法差不多:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <map>
    
    using namespace std;
    
    const int maxn = 200 + 10;
    
    struct Node {
        int from, to, next;
    }edges[maxn];
    
    int n, mem, head[maxn];
    int dp[maxn][2];
    bool dup[maxn][2];
    
    void init() {
        mem = 0;
        memset(head, -1, sizeof(head));
    }
    
    void AddEdge(int u, int v) {
        edges[mem].from = u;
        edges[mem].to = v;
        edges[mem].next = head[u];
        head[u] = mem++;
    }
    
    void dfs(int idx) {
        dp[idx][0] = 0;
        dp[idx][1] = 1;
        dup[idx][0] = dup[idx][1] = true;
    
        for(int i=head[idx]; i != -1; i = edges[i].next) {
            int v = edges[i].to;
            dfs(v);
    
            dp[idx][0] += max(dp[v][0], dp[v][1]);
            dp[idx][1] += dp[v][0];
    
            if((dp[v][0] > dp[v][1] && !dup[v][0]) ||
               (dp[v][0] < dp[v][1] && !dup[v][1]) ||
               dp[v][0] == dp[v][1])
               dup[idx][0] = false;
            if(!dup[v][0]) dup[idx][1] = false;
        }
    }
    
    int main() {
        char s1[100+10], s2[100+10];
    
        while(scanf("%d", &n) == 1 && n) {
            map<string, int> a;
            init();
            int cnt = 0;
    
            scanf("%s", s1);
    
            a[s1] = ++cnt;
    
            for(int i=1; i<n; i++) {
                scanf("%s %s", s1, s2);
    
                if(a.count(s1) == 0) a[s1] = ++cnt;
                if(a.count(s2) == 0) a[s2] = ++cnt;
    
                AddEdge(a[s2], a[s1]);
            }
    
            dfs(1);
    
            printf("%d ", max(dp[1][0], dp[1][1]));
    
            if((dp[1][1] > dp[1][0] && !dup[1][1]) ||
               (dp[1][1] < dp[1][0] && !dup[1][0]) ||
               dp[1][0] == dp[1][1])
               printf("No\n");
            else printf("Yes\n");
        }
    }
    View Code

    分析参考:http://wenku.baidu.com/view/aee323cd0508763231121252.html(第8页)

  • 相关阅读:
    Best Time to Buy and Sell Stock III
    Valid Palindrome
    Longest Substring Without Repeating Characters
    Copy List with Random Pointer
    Add Two Numbers
    Recover Binary Search Tree
    Anagrams
    ZigZag Conversion
    Merge k Sorted Lists
    Distinct Subsequences
  • 原文地址:https://www.cnblogs.com/tanhehe/p/3134672.html
Copyright © 2011-2022 走看看