HDU2883 kebab(最大流)

n位顾客要找老板烤肉串,第i个人要烤 Ni 串,每串烤 Ti 分钟,可以分成几个部分来烤,但是必须在 Si 之后才能开始烤,而且必须在 Ei 之前烤完(个人认为题目描述不清,应该是时间区间 (Si,Ei] )。烤炉每分钟可以同时烤m单位的肉串(也就是说,每个人要烤肉的总量是 NiTi 单位)。问是否能够满足所有顾客的需求。
1N200,1M1000,1Ni,Ti50,1Si<Ei1,000,000
这道题类似于HDU3572 Task Schedule,只是建图的方式不同。
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3572
题解:http://blog.csdn.net/cqbzwja/article/details/50877048
这道题不能像上面那道题一样把时间分成1分钟的小段,因为 106 太大。我们需要按照所有人的起始和终止时间排序把时间分段,一个段的时间算一个节点,这样最多只会有400个时间点。
源点s向每个人连边,容量为 NiSi 表示需要烤的总单位;每个人向对应的时间区间连边,即所有的时间段j满足 [Sj,Ei][Si,Ei] ,至于时间段j是什么,看我的代码就懂了;每个时间段向汇点连容量为 (EjSj)M 的边,表示这段时间中烤肉的最大单位数。
最后求出最大流。比较 ni=1NiSi maxflow 的大小。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define MAXN 100010
#define INF 0x3f3f3f3f
using namespace std;
inline int Min(int a,int b)
{return a<b?a:b;}
struct E
{
    int v,w,op;
    E(){}
    E(int a,int b,int c)
    {v = a; w = b; op = c;}
};
vector<E> g[MAXN];
int d[MAXN],vd[MAXN],s,t,ni[MAXN],ti[MAXN],si[MAXN],ei[MAXN];
int N,M,cnt,table[MAXN],flow,sum;
int aug(int i,int augco)
{
    int augc = augco,j,mind = t-1,delta,sz = g[i].size();
    if(i == t) return augco;

    for(j = 0; j < sz; j++)
    {
        int v = g[i][j].v;
        if(g[i][j].w)
        {
            if(d[i] == d[v]+1)
            {
                delta = Min(augc,g[i][j].w);
                delta = aug(v,delta);
                g[i][j].w -= delta;
                g[v][g[i][j].op].w += delta;
                augc -= delta;
                if(d[s] >= t) return augco - augc;
                if(augc == 0) break;
            }
            if(d[v] < mind) mind = d[v];
        }
    }
    if(augc == augco)
    {
        vd[d[i]]--;
        if(vd[d[i]] == 0) d[s] = t;
        d[i] = mind+1;
        vd[d[i]]++;
    }
    return augco-augc;
}
void sap()
{
    memset(d,0,sizeof d);
    memset(vd,0,sizeof vd);
    flow = 0;
    vd[0] = t;
    while(d[s] < t)
        flow += aug(s,INF);
}
void init()
{
    for(int i = 1; i <= N; i++)
        ni[i] = ti[i] = si[i] = ei[i] = 0;
    for(int i = 1; i <= t; i++)
        g[i].clear();
    memset(table,0,sizeof table);
    cnt = sum = 0;
}
int main()
{
    while(scanf("%d%d",&N,&M) != EOF)
    {
        s = N+1;
        for(int i = 1; i <= N; i++)
        {
            scanf("%d%d%d%d",&si[i],&ni[i],&ei[i],&ti[i]);
            table[++cnt] = si[i];
            table[++cnt] = ei[i];
            sum += ni[i]*ti[i];
            g[s].push_back(E(i,ni[i]*ti[i],g[i].size()));
            g[i].push_back(E(s,0,g[s].size()-1));
        }
        sort(table+1,table+cnt+1);
        int len = unique(table+1,table+cnt+1) - (table+1);
        t = s+len+1;
        for(int i = 1; i <= N; i++)
            for(int j = 2; j <= len; j++)
                if(si[i] <= table[j-1]&&ei[i] >= table[j])
                {
                    int x = s+j-1;
                    g[i].push_back(E(x,INF,g[x].size()));
                    g[x].push_back(E(i,0,g[i].size()-1));
                }
        for(int i = 2; i <= len; i++)
        {
            int x = s+i-1;
            g[x].push_back(E(t,(table[i]-table[i-1])*M,g[t].size()));
            g[t].push_back(E(x,0,g[x].size()-1));
        }

        sap();

        if(flow == sum) printf("Yes\n");
        else printf("No\n");
        init();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值