• 暑假集训Day8 P3472 [POI2008]MAF-Mafia(思维题)


    题目大意

    你猜呀

    输入格式

    你猜呀

    输出格式

    你猜呀

    数据范围与提示

    你接着猜呀

    算法分析

    • 设最少存活人数为MIN 最多存货人数为MAX
      来看张图:

    • 首先我们先来统计一下每个点的入度,如果一个点的入度为0,则自始至终这个人都不可能被杀(没人想杀小姐姐),就像图里的1和4,所以这个人想杀的人一定会死(震惊) 也就是图中的2

    • 每个人都会有想杀的人(咱也不知道为啥会想杀自己),一个人如果入度不为0 ,他想杀的人就不一定会死(为了保护你想杀的小姐姐 先把你干掉)
      这样的点的目标就像3,可能会死掉(如果拯救小姐姐的人来晚了,小姐姐就被大魔王煮着吃了)
      我们可以发现:

    • 如果一个点入度为0(绿框) 那么这个点一定会活下来 :MIN++ MAX++

    • 最小存活:让必定会死的人2先把自己要杀的人3杀掉(undie标记3)(在勇士救出小姐姐之前吃了她)则MIN就不能加1了

    • 最大存活:先把必定会死的人杀死防止他去杀他要杀的人(勇士在魔王吃掉小姐姐之前干掉大魔王),那么最大存活就会加1了(就像例图中3被解救了)
      But 对小姐姐图谋不轨的人远不止一个呢? 因此我们不能直接判定小姐姐是可以存活的,但是我们可以将小姐姐的入度--
      如果入度成功变成了0。 恭喜你,小姐姐得救了(虽然不能和你幸福地生活在一起),我们就可以将这个点入队了(队列维护入度为0的点,表示安全的点)

    • 但是如果这个点的入度并没有变成0 则这个点一定也会构成一个环或者链 ,关于链显然隔一个人打一个人可以存活最多的人(n/2),存活最少的人就是只活一个(很简单,自己推)

    • 如果环上有undie标记的人,可以让这个人最后死,然后在这个环死的只剩他的时候,BOOM 干掉他

    • 当然还有一种情况就是直接的无任何undie标记的环 , 同上
      1.最小存活: +1
      2.最大存活: +n/2

    • 具体的细节去看注释吧

    代码展示

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+5;
    int n,Max,Min,q[maxn],aim[maxn],rd[maxn];
    bool die[maxn],undie[maxn];
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&aim[i]);
            rd[aim[i]]++;
        }
        for(int i=1;i<=n;++i)
            if(rd[i]==0){//入度为0的点进队 
                Min++;//最小存活++
                q[++Max]=i;//最大存活++
            }
        int head=1;//模拟指针
        while(head<=Max){
            int cur=q[head];head++;//队首出队
            if(die[aim[cur]]) continue;//如果队首要杀的人已经被别人杀了
            die[aim[cur]]=1;//标记队首要杀的人
            int live=aim[aim[cur]];//队首目标的目标可死可不死
            rd[live]--;//入度--
            undie[live]=1;//可以不死了
            if(rd[live]==0)//如果入度变为0了
                q[++Max]=live;//最大存活数++,入队
        }
    //下面是处理环的
        for(int i=1;i<=n;++i)
        	if(rd[i] && !die[i]){//如果当前位置入度不为0 并且还没死
                int len=0,flag=0;//len为环的长度 flag判定当前人是否可能会死
                for(int j=i;!die[j];j=aim[j]){//遍历环
                    len++;//环长度++
                    flag|=undie[j];//当前人是否可能会死
                    die[j]=1;//标记为已死 防止下次i到这个位置再计算
                }
                if(!flag && len>1) Min++;//如果当前点不可能会死 而且环长度>1(不是自环) ,最小存活数++
                Max+=len/2;//最大存活数加上环长度的一半
        } 
        printf("%d %d",n-Max,n-Min);//要输出最小死亡数和最多死亡数
        return 0;
    }
    

    感谢观看
    点个关注>:<

  • 相关阅读:
    使用Java实现对MySql数据库的导入与导出
    【转】揭开J2EE集群的神秘面纱
    Memcached深度分析
    HSQL入门及使用指南
    系统架构基础篇(高性能基础建设说明与选型条件)
    架构之美 摘抄
    JMS规范及相关实现
    spring3中使用@value注解获取属性值
    Thread Dump 分析综述
    什么中间件及中间件服务器?
  • 原文地址:https://www.cnblogs.com/2004-08-20/p/13215979.html
Copyright © 2020-2023  润新知