HDU2883kebab(最大流ISAP)离散化思想建图

kebab

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1145    Accepted Submission(s): 472


Problem Description
Almost everyone likes kebabs nowadays (Here a kebab means pieces of meat grilled on a long thin stick). Have you, however, considered about the hardship of a kebab roaster while enjoying the delicious food? Well, here's a chance for you to help the poor roaster make sure whether he can deal with the following orders without dissatisfying the customers.

Now N customers is coming. Customer i will arrive at time si (which means the roaster cannot serve customer i until time si). He/She will order ni kebabs, each one of which requires a total amount of ti unit time to get it well-roasted, and want to get them before time ei(Just at exactly time ei is also OK). The roaster has a big grill which can hold an unlimited amount of kebabs (Unbelievable huh? Trust me, it’s real!). But he has so little charcoal that at most M kebabs can be roasted at the same time. He is skillful enough to take no time changing the kebabs being roasted. Can you help him determine if he can meet all the customers’ demand?

Oh, I forgot to say that the roaster needs not to roast a single kebab in a successive period of time. That means he can divide the whole ti unit time into k (1<=k<=ti) parts such that any two adjacent parts don’t have to be successive in time. He can also divide a single kebab into k (1<=k<=ti) parts and roast them simultaneously. The time needed to roast one part of the kebab well is linear to the amount of meat it contains. So if a kebab needs 10 unit time to roast well, he can divide it into 10 parts and roast them simultaneously just one unit time. Remember, however, a single unit time is indivisible and the kebab can only be divided into such parts that each needs an integral unit time to roast well.
 

Input
There are multiple test cases. The first line of each case contains two positive integers N and M. N is the number of customers and M is the maximum kebabs the grill can roast at the same time. Then follow N lines each describing one customer, containing four integers: si (arrival time), ni (demand for kebabs), ei (deadline) and ti (time needed for roasting one kebab well).

There is a blank line after each input block.

Restriction:
1 <= N <= 200, 1 <= M <= 1,000
1 <= ni, ti <= 50
1 <= si < ei <= 1,000,000
 

Output
If the roaster can satisfy all the customers, output “Yes” (without quotes). Otherwise, output “No”.
 

Sample Input
  
  
2 10 1 10 6 3 2 10 4 2 2 10 1 10 5 3 2 10 4 2
 

Sample Output
  
  
Yes No
 

Source
题意:有N(<=200)个顾客,第 i 个顾客现要点ni(<=50)个烤串,每个烤串需要花 ti(<=50) 个时间单位 , 服务时间从si(刚开始服务)到ei(包括ei时间点),假设一个串需要烤10个单位的时间,那么可以把这个串分成10个部分,每1部分只要1个单位的时间就可以烤好给顾客。现在烧烤架在同一时间最多可以烤M个串,问能不能服务完所有的顾客?

解析:看了网上解题,用离散化时间来建图。首先可根据题意,把每个顾客所要的ni个串分成 ni*ti 个部分,也就相当于分成ni*ti个串,每个串花1个单位时间。那么现在我们要确定流的是什么,题意中“烧烤架在 同一时间最多可以烤 M个串”,所以时每个间点与汇点建一条边,容量为M,先不看会不会超时,建立可行方案等会再来优化。确定流的是串的个数时,我们就可以把  源点 与每个顾客建一条边,边容为分解后串的个数ni*ti,最后每个顾客与服务时间内的每个时间点建一条边,边容为>=ni*ti 即可。建完后,方案是可行的,但时间点可达 100W个点,这样会超时,那么就需要用到离散化了,把每个出现的时间点记下来排序去重后,假设得k(<=400)个不同的时间点,那么就会有k-1个时间段,我们可以把每个时间段看成一个点,那么顾客与要求的时间内的时间段建边时容量可以不变(因为从每个顾客最多只需占用ni*ti 烤串位),而时间段与汇点的容量需要改动一下变为 M*(时间段的长度,不包括时间段的起始点)。 这样就建好了,点的总个数=1+n+(k-1)+1。这样便不会超时了。
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define captype int

const int MAXN = 100010;   //点的总数
const int MAXM = 400010;    //边的总数
const int INF = 1<<30;
struct EDG{
    int to,next;
    captype cap,flow;
} edg[MAXM];
int eid,head[MAXN];
int gap[MAXN];  //每种距离(或可认为是高度)点的个数
int dis[MAXN];  //每个点到终点eNode 的最短距离
int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边

void init(){
    eid=0;
    memset(head,-1,sizeof(head));
}
//有向边 三个参数,无向边4个参数
void addEdg(int u,int v,captype c,captype rc=0){
    edg[eid].to=v; edg[eid].next=head[u];
    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;

    edg[eid].to=u; edg[eid].next=head[v];
    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;
}
//预处理eNode点到所有点的最短距离
void BFS(int sNode, int eNode){
    queue<int>q;
    memset(gap,0,sizeof(gap));
    memset(dis,-1,sizeof(dis));
    gap[0]=1;
    dis[eNode]=0;
    q.push(eNode);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u]; i!=-1; i=edg[i].next){
            int v=edg[i].to;
            if(dis[v]==-1){
                dis[v]=dis[u]+1;
                gap[dis[v]]++;
                q.push(v);
            }
        }
    }
}
int S[MAXN];    //路径栈,存的是边的id号
captype maxFlow_sap(int sNode,int eNode, int n){  //注意:n为点的总个数,包括源点与汇点
    BFS(sNode, eNode);              //预处理eNode到所有点的最短距离
    if(dis[sNode]==-1) return 0;    //源点到不可到达汇点
    memcpy(cur,head,sizeof(head));

    int top=0;  //栈顶
    captype ans=0;  //最大流
    int u=sNode;
    while(dis[sNode]<n){   //判断从sNode点有没有流向下一个相邻的点
        if(u==eNode){   //找到一条可增流的路
            captype Min=INF ;
            int inser;
            for(int i=0; i<top; i++)    //从这条可增流的路找到最多可增的流量Min
            if(Min>edg[S[i]].cap-edg[S[i]].flow){
                Min=edg[S[i]].cap-edg[S[i]].flow;
                inser=i;
            }
            for(int i=0; i<top; i++){
                edg[S[i]].flow+=Min;
                edg[S[i]^1].flow-=Min;  //可回流的边的流量
            }
            ans+=Min;
            top=inser;  //从这条可增流的路中的流量瓶颈 边的上一条边那里是可以再增流的,所以只从断流量瓶颈 边裁断
            u=edg[S[top]^1].to;  //流量瓶颈 边的起始点
            continue;
        }
        bool flag = false;  //判断能否从u点出发可往相邻点流
        int v;
        for(int i=cur[u]; i!=-1; i=edg[i].next){
            v=edg[i].to;
            if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){
                flag=true;
                cur[u]=i;
                break;
            }
        }
        if(flag){
            S[top++] = cur[u];  //加入一条边
            u=v;
            continue;
        }
        //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1
        int Mind= n;
        for(int i=head[u]; i!=-1; i=edg[i].next)
        if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){
            Mind=dis[edg[i].to];
            cur[u]=i;
        }
        gap[dis[u]]--;
        if(gap[dis[u]]==0) return ans;  //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径
                                        //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流
        dis[u]=Mind+1;      //如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1
        gap[dis[u]]++;
        if(u!=sNode) u=edg[S[--top]^1].to;  //退一条边

    }
    return ans;
}

int findlocat(int *tm,int tn,int ti){
    int l=0 , r=tn-1 , mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(ti==tm[mid])return mid;
        if(ti>tm[mid]) l=mid+1;
        else r=mid-1;
    }
    return 0;
}

struct MAN{
    int sTime,eTime,kPart ;
}man[205];

int main(){
    int n,m , ni,ti;
    int s , t , ans;
    int time1[405],k;
    while(scanf("%d%d",&n,&m)>0){
        k=0; s=0; ans=0;
        init();

        for(int i=1; i<=n; i++){
            scanf("%d%d%d%d",&man[i].sTime,&ni,&man[i].eTime,&ti);

            man[i].kPart = ni*ti;
            ans += ni*ti;
            time1[k++]=man[i].sTime;
            time1[k++]=man[i].eTime;
            
            addEdg(s , i , ni*ti);
        }
        sort(time1,time1+k);
        int j=0;
        for(int i=1; i<k; i++)
            if(time1[j]!=time1[i])
            time1[++j]=time1[i];
        k=j+1;
        t=n+j+1;

        for( j=1; j<k; j++)
            addEdg(j+n , t , m*(time1[j]-time1[j-1]));

        for(int u=1; u<=n; u++){
            j=findlocat(time1 , k , man[u].sTime)+1;
            while(time1[j]<=man[u].eTime&&j<k){
                addEdg(u , j+n , man[u].kPart);
                j++;
            }
        }

        ans-=maxFlow_sap(s, t , t+1);
        printf("%s\n",ans>0?"No":"Yes");
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值