zoukankan      html  css  js  c++  java
  • 组队赛 A Promotions 深搜+逆向思维


    题意:

    一个公司总共n个人,m个关系,u v 表示u是v的上司,现在要提拔l个和r个人,要先提拔上司,上司提拔了,才能提拔下属,也就是只能先提拔入度为0的点。求在提拔l和r个人的情况下一定会提拔的人,求在r下一定不会被提拔的人。咋一看好像是拓扑,但是这题有个窍门。反过来想,现在是找出一定会被提拔的人,重复的出点没影响,如求提拔三个人下样例eve这个人。

    思路:如果提拔n个人,这个人被提拔的条件是,如果这个人不被提拔,那么他下面的人全都不会被提拔,然后剩下的人小于n,那么这个人一定可以被提拔(这个人和他下面的人为一个集合),同理r也是,至于一定不会被提拔的人,反向建图,如果这个人被提拔,那么他下面的人全都会被提拔,dfs跑出每个点所支配的人数包括自己(注意这里要加上时间戳,时间戳的值还要和vis的初始化隔开,所以加2,加1都行),所以判断的时候默认是向下的,所以是反向建图。当n-re[i]大于所要提拔的数量,当前点其实肯定不会被提拔。

    这道题就想概率中“至少”“至多”,用反向和总和相减,巧妙求出结果。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int INF = 0x3f3f3f3f;
    const int maxn = 5000+10;
    #pragma comment(linker, "/STACK:1024000000,1024000000")
    
    //正向和反向的
    vector<int>fo[maxn],re[maxn];
    int vis[maxn];
    int dfs1(int u,int timer)
    {
        //时间戳 避免重复访问,也是一个剪枝
        if(vis[u] == timer + 2  )
            return 0;
        //注意因为vis是默认初始化0的,时间戳肯定不能与0重合,因为点重0开始标记,所以这里时间戳加2与vis初始化隔开
        vis[u] = timer+2 ;//把当前点打上时间戳,回退到这个点的时候结束这一层
        int sum1=1;
        for(int i=0; i<fo[u].size(); i++)
            sum1+=dfs1(fo[u][i],timer);
        return sum1;
    }
    int dfs2(int u,int timer)
    {
        if(vis[u] == timer + 2  )
            return 0;
        vis[u] = timer + 2;
        int sum2=1;
        for(int i=0; i<re[u].size(); i++)
            sum2+=dfs2(re[u][i],timer);
        return sum2;
    }
    int main()
    {
    //    freopen("in.txt","r",stdin);
        int l,r,n,m,res[maxn];
        while(~scanf("%d%d%d%d",&l,&r,&n,&m))
        {
            memset(res,0,sizeof(res));
            memset(vis,0,sizeof(vis));
            int t1=0,t2=0,t3=0;
            for(int i=0; i<n; i++) {
                fo[i].clear();
                re[i].clear();
            }
            while(m--)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                fo[u].push_back(v),re[v].push_back(u);
            }
    
            for(int i=0; i<n; i++)
                res[i]=dfs1(i,i);
    
            for(int i=0; i<n; i++)
            {
                if(n-res[i]<l)
                    t1++;
                if(n-res[i]<r)
                    t2++;
            }
            memset(vis,0,sizeof(vis));
            for(int i=0; i<n; i++)
                res[i]=dfs2(i,i);
            for(int i=0; i<n; i++)
                if(res[i]>r)
                    t3++;
            printf("%d
    %d
    %d
    ",t1,t2,t3);
        }
        return 0;
    }
    


  • 相关阅读:
    图像修补
    图像的矩
    使用多边形将轮廓包围
    寻找物体的凸包
    查找并绘制轮廓
    重映射
    霍夫变换
    边缘检测
    第二周神经网络基础
    第一周:深度学习引言(Introduction to Deep Learning)
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/7256604.html
Copyright © 2011-2022 走看看