• 【uva1380


    【题意】 有n<=200个恰好需要一天完成的任务,要求用最少的时间完成所有任务。任务可以同时完成。但是有一些约束,分有向和无向两种,其中A-->B表示A必须在B前面完成,而A--B表示A和B不能在同一天完成。

     

    题解:最具体的题解在紫书上。。。

    如果树上的所有边都是有向边,那么答案就是最长链上的点数。

    这个显然。。因为A-->B--->C---->D就最少需要四天。。

    这样,原问题转化为:将树上所有的无向边定向,使得树上的最长链最短。

    最长链最短——二分答案。

    现在问题再次转化:给定一个x,判断是否可以给无向边定向,使得最长链的点数<=x。

     

    设定f[i]表示以i为根的子树全部定向后,最长链点数不超过x的前提下,后代到i的最长链的最小值。g[i]相反(i到后代)。

    设y为i的某个孩子。

    转化无向边的过程分为两种情况:

    a. 如果一个子树中不存在无向边,则经过子树的根结点的最长有向路结点数为f+g+1。

    b. 如果一个结点a的子结点存在无向边,则我们先递归求出所有a的子结点的f和g,然后暴力枚举将所有无向边转化为上行/下行有向边,对于每一种枚举,按照上面不存在无向边时的方法,求出f,  g和f+g+1,检查是否大于k。枚举的过程中,记下所有成功的枚举中最小的f和g,把它们和原本就是有向边子结点的最小的f和g比较,取较大值。(f和g本身是最长路的长度,这里要取的是不同成功的枚举情况下,遇到的最小的f和g。

     

    如何给边定向呢?

    但是如果我们完全枚举无向边的转换方法,则复杂度为O(2^n),n为子结点中无向边的数量。这里有一个非常棒的优化:

    求出所有子结点的f和g之后,把无向边的f和g值存到一个数组中,按照f值排序。

    之后我们从第一位开始考虑,将无向边换为下行有向边,考虑到第p位的时候,我们可以将前p-1位的无向边一并换为下行有向边。因为第p位的f值大于前p-1位无向边的f值,将前p-1位同时换为下行有向边,整个树的f值不会变(f为最长路的长度,而排序后,前p-1位形成的路都不会比第p位长),而g值有可能变小。这时,我们找出第p+1位到第n位中最大的g值,求出f+g+1,检查是否小于等于k即可。一旦有某一个p满足了要求就可以得出结果并终止枚举。复杂度为线性。

    也就是说,我们只需要枚举p,把f值前小的全部定向为y-->i。

    求g的过程相同,按照g值排序、枚举即可。

     

    为什么取的是不同情况下遇到的最小的f和g呢?

    因为对于以后要用到f[i]和g[i]的点,必定是i的父亲,它每次只用到f[i]或g[i]。

    但是对于根节点而言,它的最长链是f[i]+g[i],这里的f[i]和g[i]取了不同的情况,所以我们在求f[i]、g[i]的时候就看ff+gg+1是否<=lim,是的话才更新。也就是说,如果没有一种方案满足,f[i]和g[i]都是INF。

    所以最后判断是否可行只需要看根节点的f[i]或g[i]是否<INF即可。

     

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<algorithm>
      6 using namespace std;
      7 
      8 const int N=210,INF=(int)1e9;
      9 int n,len,tl,f[N],g[N],first[N];
     10 struct node{
     11     int x,y,next,tmp;
     12     bool bk;
     13 }a[N];
     14 struct point{
     15     int d,id;
     16 }tf[N],tg[N];
     17 
     18 int maxx(int x,int y){return x>y ? x:y;}
     19 int minn(int x,int y){return x<y ? x:y;}
     20 
     21 void ins(int x,int y,int tmp)
     22 {
     23     a[++len].x=x;a[len].y=y;a[len].tmp=tmp;a[len].bk=0;
     24     a[len].next=first[x];first[x]=len;
     25 }
     26 
     27 bool cmp(point x,point y){return x.d<y.d;}
     28 
     29 void dp(int x,int lim)
     30 {
     31     for(int i=first[x];i;i=a[i].next) dp(a[i].y,lim);
     32     tl=0;
     33     for(int i=first[x];i;i=a[i].next)
     34     {
     35         int y=a[i].y;
     36         if(a[i].tmp==2)
     37         {
     38             tl++;
     39             tf[tl].d=f[y];tf[tl].id=i;
     40             tg[tl].d=g[y];tg[tl].id=i;
     41         }
     42     }
     43     sort(tf+1,tf+1+tl,cmp);
     44     sort(tg+1,tg+1+tl,cmp);
     45     if(tl==0)
     46     {
     47         f[x]=0;g[x]=0;
     48         for(int i=first[x];i;i=a[i].next)
     49         {
     50             int y=a[i].y;
     51             if(a[i].tmp==1) f[x]=maxx(f[x],f[y]+1);
     52             if(a[i].tmp==0) g[x]=maxx(g[x],g[y]+1);
     53             if(f[x]+g[x]+1>lim) {f[x]=INF,g[x]=INF;break;}
     54         }
     55         if(!first[x]) f[x]=g[x]=INF;//debug
     56     }
     57     else
     58     {
     59         //find_f
     60         f[x]=INF;g[x]=INF;
     61         int ff,gg;
     62         for(int k=0;k<=tl;k++)
     63         {
     64             ff=0;gg=0;
     65             if(k>0) a[tf[k].id].bk=1;
     66             for(int i=first[x];i;i=a[i].next)
     67             {
     68                 int y=a[i].y; 
     69                 if(a[i].tmp==1 || (a[i].tmp==2 && a[i].bk==1)) ff=maxx(ff,f[y]+1);
     70                 if(a[i].tmp==0 || (a[i].tmp==2 && a[i].bk==0)) gg=maxx(gg,g[y]+1);
     71             }
     72             if(ff+gg+1<=lim) f[x]=minn(f[x],ff);
     73         }
     74         for(int k=1;k<=tl;k++) a[tf[k].id].bk=0;
     75         
     76         
     77         for(int k=0;k<=tl;k++)
     78         {
     79             ff=0;gg=0;
     80             if(k>0) a[tg[k].id].bk=1;
     81             for(int i=first[x];i;i=a[i].next)
     82             {
     83                 int y=a[i].y; 
     84                 if(a[i].tmp==1 || (a[i].tmp==2 && a[i].bk==0)) ff=maxx(ff,f[y]+1);
     85                 if(a[i].tmp==0 || (a[i].tmp==2 && a[i].bk==1)) gg=maxx(gg,g[y]+1);
     86             }
     87             if(ff+gg+1<=lim) g[x]=minn(g[x],gg);
     88         }
     89         for(int k=1;k<=tl;k++) a[tg[k].id].bk=0;
     90     }
     91     // printf("f %d = %d   g %d = %d
    ",x,f[x],x,g[x]);
     92 }
     93 
     94 bool check(int lim)
     95 {
     96     // printf("lim = %d
    ",lim);
     97     dp(1,lim);
     98     return (f[1]<INF);
     99 }
    100 
    101 int main()
    102 {
    103     freopen("a.in","r",stdin);
    104     int n,y;
    105     char ch;
    106     while(1)
    107     {
    108         n=0;
    109         len=0;
    110         memset(first,0,sizeof(first));
    111         while(1)
    112         {
    113             int x;
    114             scanf("%d",&x);
    115             if(x==0) break;
    116             n=maxx(n,x);
    117             while(1) 
    118             {
    119                 scanf("%d%c",&y,&ch);
    120                 n=maxx(n,y);
    121                 if(y==0) break;
    122                 if(ch=='d') ins(x,y,0);
    123                 else if(ch=='u') ins(x,y,1);
    124                 else ins(x,y,2);
    125             }
    126         }
    127         if(n==0) break;
    128         int l=0,r=n,mid;//debug 是n而不是n-1
    129         while(l<r)
    130         {
    131             mid=(l+r)/2;
    132             if(check(mid)) r=mid;
    133             else l=mid+1;
    134         }
    135         printf("%d
    ",l);
    136     }
    137     return 0;
    138 }

     

  • 相关阅读:
    dd命令测试linux磁盘读写
    centos7的启动流程
    一次服务端不响应报文情况
    主机负载高cpu使用低的一次排查
    待宰的高羊 实验六 团队作业3:团队项目需求调研与原型开发
    DOSbox中向B800段写入数据后屏幕上什么都不显示怎么办?
    用calibre传书到kindle后不显示封面怎么办?
    AURIXTC297 FreeRTOS移植工程分享
    计算机组成与体系结构
    uniapp 支付宝支付 义美
  • 原文地址:https://www.cnblogs.com/KonjakJuruo/p/5969831.html
Copyright © 2020-2023  润新知