• NOIP 2012疫情控制 (二分+倍增+贪心)


    肝了一个下午,终于把这个绝世好题写完了(滑稽)

    满分做法:

    看到题目求最短时间,说明更高的时间也可以控制,满足答案单调性,可以二分;(技巧)

    看到这些比较复杂的题目,一定要滤清自己该干什么,不要慌!!!

    显然一个军队最后停留的节点深度越小,它控制的叶子结点越多。所以我们尽量让军队往上走,如果过程中它达到了时间限制,那么就把当前节点打上驻扎标记。

    若一个军队可以走到根节点,我们让他暂停。我们记录它到达根节点后剩余的时间,可以用二元组去存储。整个过程可以用倍增去解决。

    void dfs(int x)//倍增预处理
    {
     for(int p=last[x];p;p=pre[p])
     {
       int v=other[p];
       if(v==jump[x][0])
       continue;
       jump[v][0]=x;
       dis[v][0]=len[p];
       dfs(v);    
     }
    }
    bool sta[50007];//该节点是否驻扎 
    pair<ll,int> h[50007];//二元组记录上移后闲置军队,second为上移后的节点 ,first为剩余时间 
    
      int ctot=0; 
      for(int i=1;i<=m;i++)
      {
            int x=id[i];//军队位置
        ll cnt=0;//上移时间
        for(int j=19;j>=0;j--)//倍增 
        {
            if(jump[x][j]>1&&cnt+dis[x][j]<=limit)
            {
              cnt+=dis[x][j];
              x=jump[x][j];
            }
            }
        if(jump[x][0]==1&&dis[x][0]+cnt<=limit)
        {
          h[++ctot]=make_pair(limit-dis[x][0]-cnt,x);
        }
        else sta[x]=1;
      }

    接下来判断需要驻扎的节点(与根节点直接相连),直接向下dfs即可,如果遇到有驻扎的地方就返回1。如果到叶子结点时,叶子没有驻扎

    就返回0。

    bool dfs1(int x)
    {
      bool pson=0;//判断有没有儿子
      if(sta[x]) return 1;
      for(int p=last[x];p;p=pre[p])
      {
       int v=other[p];
       if(v==jump[x][0]) continue;
       pson=1;
       if(!dfs1(v))
       return 0;
      }
      if(!pson) return 0;//如果是叶子结点且无驻扎
      return 1; 
    }
    
    bool need[50007];//此节点需不需要驻扎
    for(int p=last[1];p;p=pre[p])
      {
       int v=other[p];
       if(!dfs1(v))
       need[v]=1;
      }

    之后,该对需要驻扎的节点进行初步处理。这里用到了贪心,对于任意一个需要被驻扎的节点,若它上面停留着一支军队不能到达根节点并返回该节点,

    则令其驻扎在该节点;另外的,因为一个节点只需要一支军队驻扎,因此我们在这里选择剩余时间最小的节点,对二元组排序即可。为什么呢?因为到达根节点

    后,可以归并为同一种状态,都是从根节点向子节点走剩余的时间。因为排序后,后面的点的剩余时间大于该点,但和此点到根节点边权的大小就不得而知。但是

    我可以用较小的剩余时间去驻扎,何乐而不为呢!

    sort(h+1,h+ctot+1);
      int atot=0;
      for(int i=1;i<=ctot;i++)
      {
        if(need[h[i].second]&&h[i].first<dis[h[i].second][0])////若该军队所处的节点需要被驻扎且该军队无法到达根节点并返回    
        need[h[i].second]=0;
        else tim[++atot]=h[i].first;
      }

    下一步找到仍需要驻扎的节点,直接储存边权即可

     //找到仍需驻扎的节点
      int btot=0;
      for(int p=last[1];p;p=pre[p])
      {
       int v=other[p];
       if(need[v])
       ned[++btot]=dis[v][0];//储存从根节点到它的时间    
      }
      

    最后就是贪心匹配最终结果,排序,对于现在闲置的军队和需要被驻扎的节点,让剩余时间小的军队优先驻扎在距离根节点近的节点,这样可以保证决策最优。

    if(atot<btot)//如果剩余的军队比需要被驻扎的节点还少,则不能成功 
      return 0;
      sort(tim+1,tim+atot+1);
      sort(ned+1,ned+btot+1);
      int h=1,h1=1;
      while(h<=atot&&h1<=btot)//贪心,用剩余时间少的去覆盖 
      {
       if(tim[h]>=ned[h1])
       {
            h1++;
            h++;
       }
       else h++;
      }
      if(h1>btot) return 1;
      else return 0;

    这样这道题就轻松解决了(ε=(´ο`*)))唉)

    附上完整代码

    #include<queue>
    #include<cstring>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #include<iostream>
    using namespace std;
    const int maxm=100007;
    typedef long long ll;
    int n,m;
    int id[50007];
    int pre[maxm],last[50007],other[maxm],tot,len[maxm];
    int jump[50007][21];
    ll l,r,mid,ans;
    ll dis[50007][21];//倍增长度 
    bool sta[50007];//该节点是否驻扎 
    pair<ll,int> h[50007];//二元组记录上移后闲置军队,second为上移后的节点 ,first为剩余时间 
    ll tim[50007];//储存军队的剩余时间
    bool need[50007];
    ll ned[50007];
    void add(int x,int y,int z)
    {
     tot++;
     pre[tot]=last[x];
     last[x]=tot;
     other[tot]=y;
     len[tot]=z;    
    }
    void dfs(int x)
    {
     for(int p=last[x];p;p=pre[p])
     {
       int v=other[p];
       if(v==jump[x][0])
       continue;
       jump[v][0]=x;
       dis[v][0]=len[p];
       dfs(v);    
     }
    }
    bool dfs1(int x)
    {
      bool pson=0;
      if(sta[x]) return 1;
      for(int p=last[x];p;p=pre[p])
      {
       int v=other[p];
       if(v==jump[x][0]) continue;
       pson=1;
       if(!dfs1(v))
       return 0;
      }
      if(!pson) return 0;
      return 1; 
    }
    bool check(ll limit)
    {
      //上移 
      memset(sta,0,sizeof(sta));
      memset(tim,0,sizeof(tim));
      memset(ned,0,sizeof(ned));
      memset(h,0,sizeof(h));
      memset(need,0,sizeof(need));
      int ctot=0; 
      for(int i=1;i<=m;i++)
      {
        int x=id[i];
        ll cnt=0;//上移时间
        for(int j=19;j>=0;j--)//倍增 
        {
            if(jump[x][j]>1&&cnt+dis[x][j]<=limit)
            {
              cnt+=dis[x][j];
              x=jump[x][j];
            }
        }
        if(jump[x][0]==1&&dis[x][0]+cnt<=limit)
        {
          h[++ctot]=make_pair(limit-dis[x][0]-cnt,x);
        }
        else sta[x]=1;
      }
      //寻找需要驻扎的结点
      for(int p=last[1];p;p=pre[p])
      {
       int v=other[p];
       if(!dfs1(v))
       need[v]=1;
      }
      
      //对需要驻扎的进行初步处理
      sort(h+1,h+ctot+1);
      int atot=0;
      for(int i=1;i<=ctot;i++)
      {
        if(need[h[i].second]&&h[i].first<dis[h[i].second][0])////若该军队所处的节点需要被驻扎且该军队无法到达根节点并返回    
        need[h[i].second]=0;
        else tim[++atot]=h[i].first;
      }
      
      //找到仍需驻扎的节点
      int btot=0;
      for(int p=last[1];p;p=pre[p])
      {
       int v=other[p];
       if(need[v])
       ned[++btot]=dis[v][0];//储存从根节点到它的时间    
      }
      
      if(atot<btot)//如果剩余的军队比需要被驻扎的节点还少,则不能成功 
      return 0;
      sort(tim+1,tim+atot+1);
      sort(ned+1,ned+btot+1);
      int h=1,h1=1;
      while(h<=atot&&h1<=btot)//贪心,用剩余时间少的去覆盖 
      {
       if(tim[h]>=ned[h1])
       {
            h1++;
            h++;
       }
       else h++;
      }
      if(h1>btot) return 1;
      else return 0;
    }
    int main()
    {
      scanf("%d",&n);
      bool flag=0;
      for(int i=1;i<=n-1;i++)
      {
       int x,y,z;
       scanf("%d%d%d",&x,&y,&z);
       add(x,y,z);
       add(y,x,z);
       r+=z;
      }
      dfs(1);  
      for(int j=1;j<=19;j++)
      {
       for(int i=1;i<=n;i++)
       {
        jump[i][j]=jump[jump[i][j-1]][j-1];
        dis[i][j]=dis[i][j-1]+dis[jump[i][j-1]][j-1];
       }
      }
      scanf("%d",&m);
      for(int i=1;i<=m;i++)
      scanf("%d",id+i);
      ll l=0;
      while(l<=r)
      {
           mid=(l+r)>>1;
           if(check(mid))
           {
              ans=mid;
           r=mid-1;
           flag=1;    
           }
           else l=mid+1;
      }
      if(!flag)
      cout<<"-1"<<endl;
      else
      printf("%lld
    ",ans); 
      return 0;
    }
    View Code
  • 相关阅读:
    According to TLD or attribute directive in tag file, attribute end does not accept any expressions
    Several ports (8080, 8009) required by Tomcat v6.0 Server at localhost are already in use.
    sql注入漏洞
    Servlet—简单的管理系统
    ServletContext与网站计数器
    VS2010+ICE3.5运行官方demo报错----std::bad_alloc
    java 使用相对路径读取文件
    shell编程 if 注意事项
    Ubuntu12.04下eclipse提示框黑色背景色的修改方法
    解决Ubuntu环境变量错误导致无法正常登录
  • 原文地址:https://www.cnblogs.com/lihan123/p/11656266.html
Copyright © 2020-2023  润新知