Online Judge | Problem Set | Authors | Online Contests | User | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Web Board Home Page F.A.Qs Statistical Charts | Current Contest Past Contests Scheduled Contests Award Contest |
雁过留声——最大权封闭子图这题可以应用经典的“最大权封闭子图”来解决, 方法是借用最大流——最小割定理,转化为: 如果一个数b[i]是正的,和源点连一个容量为A[i]的边, 如果一个数b[i]是负的,和源点连一个容量为-A[i]的边, 如果一个点x依赖于点y(也就是,要辞退x必须辞退y),那么就从x向y连一条 容量∞的边。 之后,跑一遍网络流,设答案为r,则b[i]中所有正数之和-r即为所求。 这题的特殊之处就是要输出最少辞退员工数。 怎么办呢? 利用一个经典的trick:多关键字 建图前,对所有b[i],执行变换b[i]=b[i]*10000-1,然后,会惊异地发现, 此时最大流所对应的方案就是满足辞退最少人数的了。 为什么?显然,变换后的流量r2除以10000后再取整就等于原来的流量,但是 r2的后四位却蕴含了辞退人数的信息:每多辞退一个人,流量就会少1。 剩下的就是如何根据一个网络流输出方案。 我的做法:从源点开始沿着残余网络dfs(只走没有满载的边), 能dfs到的点对应的人就是需要辞退的。 网络流的算法我使用的是“有提交就会有奇迹”的dinic,实践证明,过了。 如果没有过,友情提示:动态开内存往往导致tle。 其实,写dinic是有技巧的,以下是我喜欢的19行版dinic //这是数组以及结构定义,不计算行数 int level[NMax+2],queue[NMax+2]; long long Min(long long a,long long b){ return a>b?b:a; } struct edge{ long long e,f; edge *next,*opt; }*E[NMax]; //正文 int mkLevel(){ for (int i=0;i<N;i++)level[i]=-1; int bot,x; level[queue[0]=0]=0;bot=1; for (int top=0;top<bot;top++){x=queue[top]; for (edge *p=E[x];p;p=p->next)if (p->f && level[p->e]==-1) level[queue[bot++]=p->e]=level[x]+1; } return level[N-1]!=-1; } long long extend(int x,long long alpha){ if (x==N-1)return alpha; long long r,t;r=0; for (edge *p=E[x];p;p=p->next)if (p->f && level[p->e]==level[x]+1) t=extend(p->e,Min(p->f,alpha-r)),p->f-=t,p->opt->f+=t,r+=t; if (!r)level[x]=-1; return r; } void Dinic(){while (mkLevel())extend(0,(long long)1<<60);} Followed by:
Post your reply here: |
All Rights Reserved 2003-2013 Ying Fuchen,Xu Pengcheng,Xie Di
Any problem, Please Contact Administrator