hdu 2883 (最大流)

【题意】

有一个烤肉机,每次可以同时烤M份肉。有N个顾客,第i个顾客li时刻到达,ri时刻走, 点了ai份肉,每份肉需要bi的时间烤,客人的每份肉可以分开烤,比如一份肉需要t时间烤,如果平均分出t份,那么能在1个时间内烤完。问能否满足所有顾客的需求。

【分析】

烤肉机相当于每个单位时间段都在工作,可以一直往里面加肉,每个单位时间段最多可以容下M份肉。对于每个客人,其需求需要在(li,ri)的区间内完成,因为烤肉可以分开烤,则只需考虑单位份烤肉所需时间然后累加,即只用考虑ai*bi <=(ri - li)*M,如果满足,那么这个顾客是可以满足的。显然,问题转化为区间覆盖问题。

线段上每个单位线段的容量为M,每个顾客的区间为(li,ri),其顾客容量为ai*bi。求能否覆盖完

【建图】

由以上分析,可以由源点到每个顾客i连边,容量为ai*bi;由每个单位时间段向汇点连边,容量为M;每个顾客i对每个时间段j连边,容量为INF.跑最大流判定即可。但是时间点有100W个,不可行。

【离散化】把时间点离散化成很多个区间,最多2*N-1 个区间。每个区间j为一个结点,向汇点连边(ri - li)*M。每个顾客i向每个区间j连边,如果顾客的时间段覆盖了区间,容量为INF. 类似上一题!


#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 MAXN=1010 ;
const int MAXM=200000 ; 
const int INF=999999999 ;


struct node
{
int to,c,next ;
}edge[MAXM];
int top ;
int dis[MAXN],pre[MAXN];
int gap[MAXN],head[MAXN];
int cur[MAXN],aug[MAXN];
int s[250],e[250],t[250],n[250],a[1000] ;


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

edge[top].to=u,
edge[top].c=0;
edge[top].next=head[v];
head[v]=top++;
}


int work(int S,int T,int nv)
{
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
memset(pre,0,sizeof(pre));
memset(aug,0,sizeof(aug));
int u=S,max_flow=0,v,id; 
aug[S]=INF;
pre[S]=-1;
gap[0]=nv;
for(int i = 0 ; i <= nv ;i++) cur[i]=head[i];
while(dis[S]<nv)
{
bool flag=false;
if(u==T)
{
max_flow += aug[T];
for( v = pre[T]; v!=-1;v=pre[v])
{
id = cur[v] ;
edge[id].c -= aug[T];
edge[id^1].c += aug[T];
aug[v] -= aug[T] ;
if(edge[id].c==0) u=v;

}
}
for( id = cur[u] ; id != -1 ; id=edge[id].next)
{
v=edge[id].to;
if(edge[id].c>0&&dis[u]==dis[v]+1)
{
flag=true;
pre[v]=u;
cur[u]=id;
aug[v] = min(aug[u],edge[id].c) ;
u=v;
break;
}
}
if(flag==false)
{
if(--gap[dis[u]]==0)  break; 
cur[u]=head[u];
int mindis=nv;
for( id=head[u];id!=-1;id=edge[id].next)
{
v=edge[id].to;
if(edge[id].c>0&& mindis> dis[v])
{
mindis=dis[v];
cur[u]=id;
}
}
dis[u]=mindis+1;
gap[dis[u]]++;
if(u!=S) u=pre[u];
}
}
return max_flow;
}




int main()
{
int N,M;
while(~scanf("%d%d",&N,&M))
{
      top=0;
      int sum=0,k=0;
      int S=0,T,nv;
  memset(head,-1,sizeof(head)); 
      for(int i = 1 ; i <= N ; i++)
      {
        scanf("%d%d%d%d",&s[i],&n[i],&e[i],&t[i]);
        sum += n[i]*t[i] ;
        add(S,i,n[i]*t[i]);
        a[k++]=s[i];
        a[k++]=e[i];
      }
      sort(a,a+k);   //时间离线
      T=N+k;       //汇点
      nv=T+1;     //结点数
      for(int i = 0 ; i < k-1 ; i++)
      {
        add(N+i+1,T,(a[i+1]-a[i])*M);   //每个区间结点连接汇点
        for(int j = 1 ; j <= N ;j++) 
           if(s[j]<=a[i]&&e[j]>=a[i+1])     //包含在是s[j]~e[j]间的,都可连接,
              add(j,N+i+1,INF);
      }
      if(work(S,T,nv)==sum)     
           printf("Yes\n");
      else printf("No\n");     

return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值