• 2017多校第10场 HDU 6178 Monkeys 贪心,或者DP


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6178

    题意:给出一棵有n个节点的树,现在需要你把k只猴子放在节点上,每个节点最多放一只猴子,且要求每只猴子必有一只另外的猴子通过一条边与它相连,问最少用多少条边能达到这个要求。

    解法:利用贪心的思维,显然我们应该先选择性价比最高的,即一条边连接两个点的情况。计算出这样的边的条数ans,如果ans*2>=k,结果就是(k+1)/2,否则剩下来没有安排的猴子每一只需要多一条边,即结果为ans+k-2 * ans。我的方法是用类拓扑排序的方法,找连接两个度数为1且没用被用过的点的边数,每次找到一条边后注意更新度数。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    struct FastIO
    {
        static const int S = 1310720;
        int wpos;
        char wbuf[S];
        FastIO() : wpos(0) {}
        inline int xchar()
        {
            static char buf[S];
            static int len = 0, pos = 0;
            if(pos == len)
                pos = 0, len = fread(buf, 1, S, stdin);
            if(pos == len)
                exit(0);
            return buf[pos ++];
        }
        inline unsigned long long xuint()
        {
            int c = xchar();
            unsigned long long x = 0;
            while(c <= 32)
                c = xchar();
            for(; '0' <= c && c <= '9'; c = xchar())
                x = x * 10 + c - '0';
            return x;
        }
        inline long long xint()
        {
            long long s = 1;
            int c = xchar(), x = 0;
            while(c <= 32)
                c = xchar();
            if(c == '-')
                s = -1, c = xchar();
            for(; '0' <= c && c <= '9'; c = xchar())
                x = x * 10 + c - '0';
            return x * s;
        }
        inline void xstring(char *s)
        {
            int c = xchar();
            while(c <= 32)
                c = xchar();
            for(; c > 32; c = xchar())
                * s++ = c;
            *s = 0;
        }
        inline double xdouble()
        {
            bool sign = 0;
            char ch = xchar();
            double x = 0;
            while(ch <= 32)
                ch = xchar();
            if(ch == '-')
                sign = 1, ch = xchar();
            for(; '0' <= ch && ch <= '9'; ch = xchar())
                x = x * 10 + ch - '0';
            if(ch == '.')
            {
                double tmp = 1;
                ch = xchar();
                for(; ch >= '0' && ch <= '9'; ch = xchar())
                    tmp /= 10.0, x += tmp * (ch - '0');
            }
            if(sign)
                x = -x;
            return x;
        }
        inline void wchar(int x)
        {
            if(wpos == S)
                fwrite(wbuf, 1, S, stdout), wpos = 0;
            wbuf[wpos ++] = x;
        }
        inline void wint(long long x)
        {
            if(x < 0)
                wchar('-'), x = -x;
            char s[24];
            int n = 0;
            while(x || !n)
                s[n ++] = '0' + x % 10, x /= 10;
            while(n--)
                wchar(s[n]);
        }
        inline void wstring(const char *s)
        {
            while(*s)
                wchar(*s++);
        }
        inline void wdouble(double x, int y = 6)
        {
            static long long mul[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000LL, 100000000000LL, 1000000000000LL, 10000000000000LL, 100000000000000LL, 1000000000000000LL, 10000000000000000LL, 100000000000000000LL};
            if(x < -1e-12)
                wchar('-'), x = -x;
            x *= mul[y];
            long long x1 = (long long) floorl(x);
            if(x - floor(x) >= 0.5)
                ++x1;
            long long x2 = x1 / mul[y], x3 = x1 - x2 * mul[y];
            wint(x2);
            if(y > 0)
            {
                wchar('.');
                for(size_t i = 1; i < y && x3 * mul[i] < mul[y]; wchar('0'), ++i);
                wint(x3);
            }
        }
        ~FastIO()
        {
            if(wpos)
                fwrite(wbuf, 1, wpos, stdout), wpos = 0;
        }
    } io;
    const int maxn = 100010;
    struct edge{
        int to,next;
    }E[maxn*2];
    int head[maxn],edgecnt;
    void init(){
        edgecnt=0;
        memset(head,-1,sizeof(head));
    }
    void add(int u, int v){
        E[edgecnt].to=v,E[edgecnt].next=head[u],head[u]=edgecnt++;
    }
    int du[maxn];
    bool vis[maxn];
    
    int main()
    {
        int T,n,k;
        T = io.xuint();
        while(T--)
        {
            init();
            memset(vis,0,sizeof(vis));
            n = io.xuint();
            k = io.xuint();
            for(int i=2; i<=n; i++){
                int x;
                x = io.xuint();
                add(i, x);
                add(x, i);
                du[x]++;
                du[i]++;
            }
            queue<int>q;
            for(int i=1; i<=n; i++){
                if(du[i] == 1){
                    q.push(i);
                }
            }
            int ans = 0;
            while(!q.empty())
            {
                int u = q.front(); q.pop();
                for(int i=head[u]; i+1; i=E[i].next){
                    int v = E[i].to;
                    if(!vis[u]&&!vis[v]){
                        vis[u]=1;
                        vis[v]=1;
                        ans++;
                    }
                    du[v]--;
                    if(du[v]==1){
                        q.push(v);
                    }
                }
            }
            if(ans*2>=k){
                printf("%d
    ", (k+1)/2);
            }
            else{
                ans = ans+k-2*ans;
                printf("%d
    ", ans);
            }
        }
        return 0;
    }
    

    我还在网上看到了另外一种利用树形DP解决这个题的方法,觉得挺妙的,他的方法是这样的。http://blog.csdn.net/my_sunshine26/article/details/77586785

    我们尽量要使一个点只匹配一个点,很容易想到二分匹配中的最大匹配数,而最大匹配数即为最小点覆盖数,可用树形DP解决。

    我们用dp[i][1]表示选择i这个顶点(初始化dp[root][1]为1)的最小点覆盖数,那么它的儿子节点son[i]可以被选择也可以不被选择。

    状态转移方程为 dp[i][1]+=min(dp[son[i]][0],dp[son[i]][1]);

    用dp[i][0]表示不选择i这个顶点(初始化dp[root][0]为0)的最小点覆盖数,显然它的儿子节点son[i]就必须被选择了。

    状态转移方程为 dp[i][0]+=dp[son[i]][1];

    最终求出最小点覆盖数(最大匹配数)即为min(dp[1][0],dp[1][1]),也就求出了ans,然后跟上面的步骤相同。




  • 相关阅读:
    sql server日志已满报错
    图片基础信息
    android小细节
    内存泄露分析
    一个非常快的android模拟器
    activity退出
    ListView中内容的动画效果
    视频相关android软件
    Android Screen Monitor抓取真机屏幕
    ListView中使用type需要注意的东西
  • 原文地址:https://www.cnblogs.com/spfa/p/7439795.html
Copyright © 2020-2023  润新知