• HDU 6178 Monkeys


    Monkeys

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 153428/153428 K (Java/Others)
    Total Submission(s): 1328    Accepted Submission(s): 435


    Problem Description
    There is a tree having N vertices. In the tree there are K monkeys (K <= N). A vertex can be occupied by at most one monkey. They want to remove some edges and leave minimum edges, but each monkey must be connected to at least one other monkey through the remaining edges.
    Print the minimum possible number of remaining edges.
     
    Input
    The first line contains an integer T (1 <= T <= 100), the number of test cases. 
    Each test case begins with a line containing two integers N and K (2 <= K <= N <= 100000). The second line contains N-1 space-separated integers a1,a2,,aN1, it means that there is an edge between vertex ai and vertex i+1 (1 <= ai <= i).
     
    Output
    For each test case, print the minimum possible number of remaining edges.
     
    Sample Input
    2 4 4 1 2 3 4 3 1 1 1
     
    Sample Output
    2 2
     
    Source
     
    Recommend
    liuyiding   |   We have carefully selected several similar problems for you:  6216 6215 6214 6213 6212 
     
    思路:贪心+二分图最大匹配(树形DP)

    这题O(n)做竟然卡读入优化。。。。。。。。。。。。。。。。。。。。。。用一般的读入优化竟然TLE。

    从大佬哪里求得黑科技,样例输不进去竟然AC了:

    namespace IO  
    {  
        const int U=40*1024*1024;  
        char buf[U];
        int ptr,sz;
        void begin()
        {
            ptr=0;
            sz=fread(buf,1,U,stdin);
        }
        template<typename T>
        inline bool scan_d (T&t)
        {
            while(ptr<sz&&buf[ptr]!='-'&&(buf[ptr]<'0'||buf[ptr]>'9'))    ptr++;
            if(ptr>=sz)    return false;
            bool sgn=false;
            if(buf[ptr]=='-')    sgn=true,ptr++;
            for(t=0;ptr<sz&&'0'<=buf[ptr]&&buf[ptr]<='9';ptr++)  
                t=t*10+buf[ptr]-'0';
            if(sgn)    t=-t;
            return true;
        }  
    }  
    using namespace IO;
    View Code

    ①很显然,如果我们最终的答案是一个包含K个点的整个联通块的话,显然答案就是K-1.如果我们是两个共包含K个点的联通块的话,显然答案会对应减少。

    所以我们希望分部的情况是尽可能多的联通块,那么理应我们希望将结果分成若干个两两相连的小联通块。

    ②我们希望构成尽可能多的这样两两相连的小联通块(只用一条边去连接)的话,很显然是需要跑最大二分匹配数。我们知道最小点覆盖==最大二分匹配数,而直接建图跑二分图匈牙利匹配的话,时间复杂度很爆炸,我们知道树形dp可以O(n)求树上的最小点覆盖问题,所以我们直接跑树形Dp即可。

    ③如果我们最小点覆盖数为ans个,那么分情况讨论即可:

    如果k<=ans*2,那么结果就是k/2

    如果k>ans*2,那么结果就是k-ans*2+ans(多出来的点直接往上加即可)

    如果k是奇数,那么答案再加1.

     

    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define MAXN 100010
    using namespace std;
    int T,n,k,tot;
    int dp[MAXN][2];
    vector<int>vec[MAXN];
    namespace IO  
    {  
        const int U=40*1024*1024;  
        char buf[U];
        int ptr,sz;
        void begin()
        {
            ptr=0;
            sz=fread(buf,1,U,stdin);
        }
        template<typename T>
        inline bool scan_d (T&t)
        {
            while(ptr<sz&&buf[ptr]!='-'&&(buf[ptr]<'0'||buf[ptr]>'9'))    ptr++;
            if(ptr>=sz)    return false;
            bool sgn=false;
            if(buf[ptr]=='-')    sgn=true,ptr++;
            for(t=0;ptr<sz&&'0'<=buf[ptr]&&buf[ptr]<='9';ptr++)  
                t=t*10+buf[ptr]-'0';
            if(sgn)    t=-t;
            return true;
        }  
    }  
    using namespace IO;
    void dfs(int fa,int now){
        dp[now][0]=0;
        dp[now][1]=1;
        for(int i=0;i<vec[now].size();i++)
            if(vec[now][i]!=fa){
                dfs(now,vec[now][i]);
                dp[now][0]+=dp[vec[now][i]][1];
                dp[now][1]+=min(dp[vec[now][i]][0],dp[vec[now][i]][1]);
            }
    }
    int main(){
        IO::begin();
        scan_d(T);
        while(T--){
            tot=0;
            memset(dp,0,sizeof(dp));
            scan_d(n);scan_d(k);
            for(int i=1;i<=n;i++)    vec[i].clear();
            for(int i=1;i<n;i++){
                int x;
                scan_d(x);
                vec[x].push_back(i+1);
                vec[i+1].push_back(x);
            }
            dfs(0,1);
            int ans=min(dp[1][0],dp[1][1]);
            if(ans*2>=k)    printf("%d
    ",(k+1)/2);
            if(ans*2<k)    printf("%d
    ",ans+k-2*ans);
        }
    }
    细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。 雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。
  • 相关阅读:
    界面版按键精灵的使用【包含内置浏览器、打开程序的方法】
    按键精灵【找图片,并打开该图或打开且关闭两段代码】
    tomcat零碎知识点
    windows server 2008运维
    上传文件与读取文件列表以及创建文件
    Tomcat和win2008运维常识及方法
    Linux权限问题
    Summary
    iOS 高阶
    iOS
  • 原文地址:https://www.cnblogs.com/cangT-Tlan/p/7580113.html
Copyright © 2020-2023  润新知