题目意思:
给出 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;
}