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

Posted Lsxxxxxxxxxxxxx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\n", (k+1)/2);
        }
        else{
            ans = ans+k-2*ans;
            printf("%d\n", 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,然后跟上面的步骤相同。




以上是关于2017多校第10场 HDU 6178 Monkeys 贪心,或者DP的主要内容,如果未能解决你的问题,请参考以下文章

2017多校第10场 HDU 6181 Two Paths 次短路

2017多校第10场 HDU 6180 Schedule 贪心,multiset

2017多校第6场 HDU 6105 Gameia 博弈

2017多校第9场 HDU 6170 Two strings DP

2017多校第4场 HDU 6078 Wavel Sequence DP

2017多校第8场 HDU 6133 Army Formations 线段树合并