HDU 3572 (最大流构图)

题目意思:

给出 N 件任务和 M台机器, 这N件任务都一个限制: 必须在 [S,E] 之间完成, 而且完成的时间不能超过 P.

一台机器每天只能做意见任务, 不过庆幸的是: 任务是可以拆分的, 比如一件任务要3天完成, 那么你就可以将呀拆分

成3份. 现在问: 在所有机器慢负荷运转的情况下, 如何分配这些任务使得在最后的期限时, 所有任务都能完成.

解题 :

仔细分析下题目不难想到是个网络流模型, 问题就是求源点的流是否能够全部流到汇点. 关键在于构图. 我们选取一个

超级源点和一个超级汇点, 一开始把源点指向所有的任务, 边权就是完成这件任务需要的天数, 然后按照完成这件任务

的时间区间, 将任务分成 E-S+1份, 意思就是在这几天中每天都可以完成这件任务的一份.这样, 就可以在任务和能够完

成它的这些天之间连边, 边权为1, 因为每次只能做一份, 最后在所有的天和汇点之间连边, 边权为M, 表示每一天, M台

机器可以完成M份工作:

相当于从原点S流出 P=p1+p2+p3+----+pn件任务;流到汇点T的是M台机器人在D天里一共完成的任务;所以只要求该网络的最大流,若恰好等于P ,则完成任务;注意D天是所有s->e 中最大的e ;



#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=1111 ;
const int M=505000 ;
const int INF = 1<<30 ;
struct node
{
int u,v,c ,next ;
}edge[M] ;
int top ;
int dis[N],pre[N];
int aug[N],cur[N];
int head[N],gap[N];


int work(int s , int  t , int n)
{
int max_flow=0 , u=s , v , id  ,mindis ;
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
memset(pre,0,sizeof(pre));
memset(aug,0,sizeof(aug));
aug[s]=INF;            //aug[i],是在i点的可修改增量
pre[s]=-1;              //增广路的路径
gap[0]=n;             //其实可有可无,
for(int i = 0 ; i <= n ; i++)   //因为每个点炼油很多条弧;
   cur[i]=head[i];              //初始化,使第一条弧为改点的允许弧,
    while(dis[s]<n)               //dis[i],是层次网络中到汇点的距离或层次
{
 bool flag=false ;
if(u==t)                   //若找到汇点
{
max_flow += aug[t] ;       //汇点的可修改增量加入到最大流中
for(v=pre[t]; v!=-1;v=pre[v])   //回溯,修改调整这条增广路。
{
id = cur[v] ;                  //v点的允许弧是edge[id] ,即该弧在增广路上
edge[id].c -= aug[t] ;    //残留网络中修改弧上的可增量
edge[id^1].c += aug[t] ;  //反向加 。
aug[v] -= aug[t] ;         //v点的可修改增量减少 ;
if(edge[id].c==0) u=v ;    //如果该弧已经满了,则不回退到汇点,从改点继续找;
}  
}
for( id = cur[u] ; id!=-1 ; id = edge[id].next)   //在u点所有弧上,找到 允许弧;
{
  v= edge[id].v ;
  if(edge[id].c>0 && dis[u]==dis[v]+1)  //允许弧可增量大于0,且在层次网络中;
  {
        flag=true ;       //标记找到允许弧
        pre[v]=u;
        cur[u]=id;       //更新,u点上的允许弧是edge[id] ;
       aug[v] = min(aug[u],edge[id].c);  //找的过程中,一路更新增广路上的最小修改增量 ;找到汇点t时,即为修改增量;
       u=v;                                           //更新u ; 增广路已经找到v这点了;
       break ;
  }
}
if(flag==false)      //如果在u点找不到允许弧,即u点以后不通了;
{
if(--gap[dis[u]]==0)  break;  //gap[i],表示在层次网络的第i层就断了,用来优化的,也可没有;
mindis = n ;                        //
cur[u]=head[u];
for( id=head[u] ; id != -1 ; id = edge[id].next)  //在u点的所有弧上,找符合要求的弧,修改层次距离dis[u] ;
{
 v= edge[id].v ;
  if( edge[id].c>0&& dis[v] < mindis) //找到dis[v]最小的那条弧,
  {
          mindis = dis[v] ;
          cur[u] = id ;             //标记u点上的符合要求要修改的弧是edge[id],
  }
}
  dis[u]=mindis+1 ;      //修改dis[u]
  gap[dis[u]]++ ;       //标记u点在第dis[u]层断了,找不下去,
 if(u!=s) u=pre[u] ;   //退一步继续找 ;

}
return max_flow ;
}

void add(int u,int v ,int c)   //邻接表
{
edge[top].u=u;
edge[top].v=v;
edge[top].c=c;
edge[top].next=head[u];
head[u]=top++;

edge[top].u=v;
edge[top].v=u;
edge[top].c=0;
edge[top].next=head[v];
head[v]=top++;
}
int main()
{
int n,m,tt,pi,si,ei,sum,maxday,s,t,cas ,nv;
cin>>tt ;
for(int cas = 1 ; cas <= tt ; cas++)
{
top=0;
memset(head,-1,sizeof(head));
sum=0;maxday=0;s=0;t=0;
cin>>n>>m ;
for(int i = 1 ; i <= n  ; i++)
{
cin>>pi>>si>>ei ;
sum += pi ;
maxday = max(maxday,ei);
add(s,i,pi);
for(int j = si ; j <= ei ; j++)
   add(i,n+j,1);
}
t=n+maxday+1;
nv=t+1 ;
for(int i = 1 ; i <= maxday ; i++)
   add(n+i,t,m) ;
 if(work(s,t,nv)==sum)
    printf("Case %d: Yes\n\n",cas);
      else  
         printf("Case %d: No\n\n",cas);
}
return 0;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值