HDU3572 :Task Schedule

题目: Our geometry princess XMM has stoped her study in computational geometry to concentrate on her newly opened factory. Her factory has introduced M new machines in order to process the coming N tasks. For the i-th task, the factory has to start processing it at or after day Si, process it for Pi days, and finish the task before or at day Ei. A machine can only work on one task at a time, and each task can be processed by at most one machine at a time. However, a task can be interrupted and processed on different machines on different days.
Now she wonders whether he has a feasible schedule to finish all the tasks in time. She turns to you for help.

Input:On the first line comes an integer T(T<=20), indicating the number of test cases.You are given two integer N(N<=500) and M(M<=200) on the first line of each test case. Then on each of next N lines are three integers Pi, Si and Ei (1<=Pi, Si, Ei<=500), which have the meaning described in the description. It is guaranteed that in a feasible schedule every task that can be finished will be done before or at its end day.

output:For each test case, print “Case x: ” first, where x is the case number. If there exists a feasible schedule to finish all the tasks, print “Yes”, otherwise print “No”.
Print a blank line after each test case.

Sample Input
4 3
1 3 5
1 1 4
2 3 7
3 5 9

2 2
2 1 3
1 2 2

Sample Output
Case 1: Yes

Case 2: Yes

题目描述:有 N个任务,M台机器,每个任务有一个开始时间 S 和截止时间 E 以及需要完成的时间P(需要P天才能完成),一个机器一次只能执行一个任务,一个任务一次也只能 在一台机器上执行,但完成一个任务可分多次在不同的时间(在不同天数),不同的机器上执行。问有没有能给定完成任务的机器工作安排方案。

分析:主要是如何建图。将每个任务抽象成编号1到N的点。与源点S建权值为P的边(表示每个任务需要P天完成),再将每个任务与该任务可以开始执行的日期(从S 到 E 中间每一个值 i,抽象为点N+i)建权值为1的边 (在这天执行这个任务一天)。最后需要一个总汇点 (max(E)+N+1),与前面所有任务可执行天数建权值为m的边。

如下图 为第一个样例的图(手残画图,看得懂就行)
在这里插入图片描述
到这一步就很清晰了。每一天都有机器可以执行任务(从5到13)。从0点流入,最终流向14号点的一条增广路径, 表示的是一次机器工作的合法安排。如在5号点流向14号点。最大不能超过m表示这一天工作的机器不能超过m个。如果从0号点经5号点能流通到14号点,则表示5号点表示的这一天机器安排是合法的(不会同时使用超过m台机器)。1号点最大可增广流的值为1号任务可执行的最大次数(这个值不超过P因为只需要P次1号任务就完成了),2,3,4号点同理。
0号点的增广流的值为执行所有任务最大次数(执行一天算一次,总共只需要执行p1+p2+…+pn次所有任务就执行完了).

因此只要判断0号点最大流的值是否等于p1+p2+…+pn(判断是否可以满流)。这题就解决了。

下面给出我写的两个算法(ISAP和dinic)代码:

dinic:

#include<iostream>
using namespace std;
#include<stdio.h>
#include<string.h>
#include<queue>
#define INF 0x3f3f3f3f
struct EDG
{
 int v,w,nxt;
};
int cnt;
int head[2000];
EDG edg[1000000];
int deep[2000];
int T,N,M;
void init()
{
 cnt=0;
 memset(head,-1,sizeof(head));
}
void addedg(int x,int y,int w)
{
 edg[cnt].v=y;
 edg[cnt].w=w;
 edg[cnt].nxt=head[x];
 head[x]=cnt++;
}
int bfs(int s,int t)
{
 queue<int> pq;
 memset(deep,0,sizeof(deep));
 pq.push(s);
 deep[s]=1;
 int top;
 while(!pq.empty())
 {
  top=pq.front();
  pq.pop();
  for(int i=head[top];i+1;i=edg[i].nxt)
  {
   int v=edg[i].v;
   int w=edg[i].w;
   if(!deep[v]&&w)
   {
    deep[v]=deep[top]+1;
    pq.push(v);
   }
  }
 }
 return deep[t];
}
int dfs(int s,int t,int inflow)
{
 if(s==t)
  return inflow;
 int acflow=0;
 for(int i=head[s];i+1;i=edg[i].nxt)
 {
  if(edg[i].w&&deep[s]+1==deep[edg[i].v])
  {
   int x=dfs(edg[i].v,t,min(inflow,edg[i].w));
   if(x>0)
   {
    acflow+=x;inflow-=x;edg[i].w-=x;edg[i^1].w+=x;
    if(!inflow)
     break;
   }
  }
 }
 if(!acflow)
  deep[s]=-2;
 return acflow;
}
int dinic(int s,int t)
{
 int f=0;
 while(bfs(s,t))
  f+=dfs(s,t,INF);
 return f;
}
int main()
{
 scanf("%d",&T);
 for(int i=1;i<=T;i++)
 {
  init();
  scanf("%d%d",&N,&M);
  int P,S,E;
  int et=0,sump=0;
  int tl=INF,tr=0;
  for(int k=1;k<=N;k++)
  {
   scanf("%d%d%d",&P,&S,&E);
   sump+=P;
   if(tl>S)
    tl=S;
   if(E>tr)
    tr=E;
   addedg(0,k,P);
   addedg(k,0,0);
   for(int j=S;j<=E;j++)
   {
    addedg(k,j+N,1);
    addedg(j+N,k,0);
   }
  }
  et=tr+N+1;
  for(int j=tl;j<=tr;j++)
  { 
   addedg(j+N,et,M);
   addedg(et,j+N,0);
  }
  printf("Case %d: %s\n\n",i,dinic(0,et)==sump?"Yes":"No");
 }
 return 0;
}

ISAP:

#include<iostream>
using namespace std;
#include<stdio.h>
#include<string.h>
#include<queue>
#define INF 0x3f3f3f3f
struct EDG
{
 int v,w,nxt;
};
int cnt;
int head[2000];
EDG edg[1000000];
int deep[2000];
int gap[2000];
int T,N,M;
void init()
{
 cnt=0;
 memset(head,-1,sizeof(head));
}
void addedg(int x,int y,int w)
{
 edg[cnt].v=y;
 edg[cnt].w=w;
 edg[cnt].nxt=head[x];
 head[x]=cnt++;
}
void bfs(int s,int t)
{
 queue<int> pq;
 memset(deep,0,sizeof(deep));
 memset(gap,0,sizeof(gap));
 pq.push(t);
 deep[t]=1;
 gap[deep[t]]++;
 int top;
 while(!pq.empty())
 {
  top=pq.front();
  pq.pop();
  for(int i=head[top];i+1;i=edg[i].nxt)
  {
   int v=edg[i].v;
   int w=edg[i].w;
   if(!deep[v]&&!w)
   {
    deep[v]=deep[top]+1;
    gap[deep[v]]++;
    pq.push(v);
   }
  }
 }
}
int dfs(int s,int t,int inflow)
{
 if(s==t)
  return inflow;
 int acflow=0;
 int f=0;
 for(int i=head[s];i+1;i=edg[i].nxt)
 {
  if(edg[i].w>0&&deep[s]==deep[edg[i].v]+1)
  {
   f=1;
   int x=dfs(edg[i].v,t,min(inflow,edg[i].w));
   acflow+=x;inflow-=x;edg[i].w-=x;edg[i^1].w+=x;
   if(!inflow)
    break;
  }
 }
 if(!f)
 {
  gap[deep[s]]--;
  if(gap[deep[s]]==0)
   deep[0]=INF;
  else
  {
   deep[s]++;
   gap[deep[s]]++;
  }
 }
 return acflow;
}
int ISAP(int s,int t)
{
 bfs(s,t);
 int f=0;
 while(deep[0]<INF)
  f+=dfs(s,t,INF);
 return f;
}
int main()
{
 scanf("%d",&T);
 for(int i=1;i<=T;i++)
 {
  init();
  scanf("%d%d",&N,&M);
  int P,S,E;
  int et=0,sump=0;
  int tl=INF,tr;
  for(int k=1;k<=N;k++)
  {
   scanf("%d%d%d",&P,&S,&E);
   sump+=P;
   if(E>et)
    et=E;
   if(tl>S)
    tl=S;
   addedg(0,k,P);
   addedg(k,0,0);
   for(int j=S;j<=E;j++)
   {
    addedg(k,j+N,1);
    addedg(j+N,k,0);
   }
  }
  tr=et;
  et=et+N+1;
  for(int j=tl;j<=tr;j++)
  { 
   addedg(j+N,et,M);
   addedg(et,j+N,0);
  }
  printf("Case %d: %s\n\n",i,ISAP(0,et)==sump?"Yes":"No");
 }
}

用ISAP的话最大层次可以设置为INF(或者N+E+2),加入gap优化能跑出来(156ms)。虽然从建图过程来看我们建的图只有4层(原点到任务点,任务点到可执行天数点,天数点到总汇点)。但是while(deep[0]<=4)的写法是错的(WA了很多发,我也不知道为什么):有清楚的可以评论回复下。

dinic单路增广直接超时,多路增广124ms(多次提交都比ISAP快一点点)。

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/83715007