• 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);
        }
    }
    细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。 雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。
  • 相关阅读:
    学校重理论,公司重操作,计算机专业毕业生该何去何从?
    最简单的ajax示例
    几个简单的例子让你读懂什么是JAVA的堆栈跟踪
    避免在JSP中写java代码
    ==和equals()的区别
    测试,我误解了你
    项目管理十大TION法
    Web测试与APP测试有哪些异同?
    spring cglib 与 jdk 动态代理
    java.util.concurrent.Semaphore 使用
  • 原文地址:https://www.cnblogs.com/cangT-Tlan/p/7580113.html
Copyright © 2020-2023  润新知