1. 程式人生 > >HDU--杭電--3572--Task Schedule--最大流

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賣萌充當學霸,但是給我的收貨是學演算法不光是瞭解他的用法和熟練去寫它,還需要充分了解它的部分原理,學習其中一些深層的卻不起眼的東西,往往這知識點關鍵時候就是你制勝的關鍵··