HDU--杭电--3572--Task Schedule--最大流

Task Schedule

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5031    Accepted Submission(s): 1642


Problem Description
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
  
  
2 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
 

题意:给你m个机器和n个任务,每个机器一次只能运行一个任务,但是可以运行中断换任务,每个任务有一个开始的最早时间Si和完成的最晚时间Ei,这个任务完成需要的时间Pi,问是否可以完成所有任务。。

*****这个题比较难想到网络流,但是想到点上了就发现这就是个网络流,我开始用最初学的那个算法写,结果CE,不明就里,就改啊改的,然后是RE,说是下标越界,再改就成了TLE,超时。。。然后临时在网上复习了一下dinic算法,这个耗时小一些。。。

题解:和普通网络流一样,设置一个超级原点S和超级汇点T,S连接每个任务,流量限制为这个任务的Pi,T连接每个时间点,流量限制为m,因为同一时刻m个机器是能同时运行的,然后把每个任务同它相关的时间点都连接起来,流量限制为1,然后进行你的网络流模版就是了~~

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define MAX 1000000001
#define Max(a,b) a>b?a:b
#define Min(a,b) a<b?a:b
#define M 1111
using namespace
std;

int
n,m,sum;
struct
node
{

    int
to,val,next;//流向的点,流量限制,下一个对应的点
}
s[M*M];//其实就类似做一个线性表

int
hand[M],snum;//hand[x]表示x在之前最后出现的那次,在s中的位置,snum是s的长度
int
dis[M],q[M];//dis记录dinic算法中用bfs分的层,q是模拟队列
void
setit(int from,int to,int val)
{

    s[snum].to=to;//正向,流量限制为输入的这个
    s[snum].val=val;
    s[snum].next=hand[from];
    hand[from]=snum++;
    s[snum].to=from;//反向 流量限制初始化为0
    s[snum].val=0;
    s[snum].next=hand[to];
    hand[to]=snum
++;
}

int
bfs()
{

    int
i,j,k,l,cur,qian,hou;
    memset(dis,-1,sizeof(dis));
    qian=hou=0;
    q[qian++]=0;
    dis[0]=0;

    while
(qian!=hou)//判断模拟循环队列是否为空
    {

        cur=q[hou++];//取队首元素并出队

        if
(hou>=M)hou=0;//循环队列
        for
(i=hand[cur];i!=-1;i=s[i].next)//线性表中快速遍历与cur相关的点
        {

            k=s[i].to;//取出点

            if
(s[i].val>0&&dis[k]==-1)//判断流量限制和是否已经分层
            {

                dis[k]=dis[cur]+1;//分层
                q[qian++]=k;//入队

                if
(qian>=M)qian=0;//循环队列
                if
(k==1001)return 1;//已经分层汇点
            }
        }
    }

    return
0;//到最后都没有搜到汇点表示没有增广路了
}

int
dfs(int x,int flow)
{

    int
i,j,k,l,cost=0;
    if
(flow<=0)return 0;
    if
(x==1001)return flow;//汇点
    for
(i=hand[x];i!=-1;i=s[i].next)//遍历相关点
    {

        k=s[i].to;//取点

        if
(s[i].val>0&&dis[k]==dis[x]+1)//判断流量限制和层次关系是否符合
        {

            l=dfs(k,Min(flow-cost,s[i].val));//向下搜索,流量限制取当前剩余流量和路径限制流量中的最小值

            if
(l>0)//向下搜到汇点则确定这一条路径
            {

                cost+=l;//当前路径的最小流量加入到总流量消耗值中
                s[i].val-=l;//正向减少
                s[i^1].val+=l;//反正增加

                if
(cost==flow)break;//当前点满流则结束
            }
else dis[k]=-1
;//向下搜不到汇点则切断这条路,相当于深搜中vis的作用
        }
    }

    return
cost;
}

void
dinic()
{

    int
i,j,k,l=0;
    while
(bfs())//分层并判断原点是否与汇点相通
    {

        l+=dfs(0,MAX
);//深搜在当前分层中找出最大流
    }

    if
(l>=sum)puts("Yes");//最大流可能大于预判,因为任务在最大时间点之前就全部完成了
    else
puts("No");
}

int
main (void)
{

    int
t,cas=1,i,j,k,l,Pi,Si,Ei;
    scanf("%d",&t);

    while
(t--&&scanf("%d%d",&n,&m))
    {

        snum=sum=0;
        memset(hand,-1,sizeof(hand));

        for
(i=1;i<=n;i++)
        {

            scanf("%d%d%d",&Pi,&Si,&Ei);
            setit(0,i,Pi);//我设置的是0为原点,1到n是任务,这里是连接原点跟任务
            sum+=Pi;//记录总花费时间

            while
(Si<=Ei)//遍历当前任务相关的所有时间点
            {

                setit(i,500+Si,1);//最大任务数是500,所以时间我设置是501到1000,这里是连接任务和相关时间
                Si
++;
            }
        }

        for
(i=501;i<=1000;i++)
        setit(i,1001,m);//我设置的汇点是1001,这里是连接所有时间点跟汇点(不管是否在任务范围,这是为了偷懒和方便)
        printf("Case %d: ",cas++);
        dinic();
        puts(""
);//格式。。
    }

    return
0;
}

心得:我也是醉了,开始绘图错了,到后来算法时间度我没有深入了解,以至于到了大半夜还在装13卖萌充当学霸,但是给我的收货是学算法不光是了解他的用法和熟练去写它,还需要充分了解它的部分原理,学习其中一些深层的却不起眼的东西,往往这知识点关键时候就是你制胜的关键··

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值