jzoj3614 [NOI2014模拟] 加工安排 最大流

31 篇文章 0 订阅

Description


小K爸爸的工厂最近生意红火!小K也利用自己的所学所能帮助他的父亲。

有N位客户希望工厂为他们加工产品。每位客户都提供了需要加工的产品的类型,产品到达工厂的时间s和最迟完成加工的时间t。小K根据需要加工的产品类型预计了每个产品加工所需的时间c(时间i可以认为是第i分钟开始的时刻)。工厂里的生产车间一共有M台机器。每个产品在每台机器上都可以加工,但是,一台机器在任何时候最多只能加工一件产品,而一件产品在任何时候也最多只能被一台机器加工。同时,我们可以在某台机器正在加工时将工作打断,换另一个产品加工。小K希望帮助他父亲计算一下,能否找到一个方案,使得所有的产品都在规定的时间内完成加工?

N(1<=N<=100)
M(1<=M<=10)
c[i],s[i],t[i]均在100000以内

Solution


一个朴素的想法就是把所有长度为1的时间拆成点,连向可以加工的产品,容量为1。所有单位时间连汇点容量为1*m(m台机器可以同时工作),源点连产品容量为c,若跑出来的最大流等于sumC则Yes

仔细观察发现数据中n极小,可以把时间点离散然后相邻时间点组成的一段时间压成一个点,容量就是它的长度了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
const int INF=0x3f3f3f3f;
const int N=400005;
const int E=800005;
struct edge{int x,y,w,next;}e[E];
struct seg{int l,r,len;}s[N];
std:: queue<int> que;
int cur[N],ls[N],edCnt;
int dis[N],l[N],r[N],c[N];
bool rec[N];
int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}
void addEdge(int x,int y,int w) {
    e[++edCnt]=(edge){x,y,w,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge){y,x,0,ls[y]}; ls[y]=edCnt;
    // printf("%d %d %d\n", x,y,w);
}
int bfs(int st,int ed) {
    while (!que.empty()) que.pop();
    que.push(st);
    fill(dis,-1); dis[st]=1;
    while (!que.empty()) {
        int now=que.front(); que.pop();
        for (int i=ls[now];i;i=e[i].next) {
            if (dis[e[i].y]==-1&&e[i].w>0) {
                dis[e[i].y]=dis[now]+1;
                if (e[i].y==ed) return 1;
                que.push(e[i].y);
            }
        }
    }
    return 0;
}
int find(int now,int ed,int mn) {
    if (now==ed||!mn) return mn;
    int ret=0;
    for (int &i=cur[now];i;i=e[i].next) {
        if (e[i].w>0&&dis[now]+1==dis[e[i].y]) {
            int d=find(e[i].y,ed,std:: min(mn-ret,e[i].w));
            e[i].w-=d; e[i^1].w+=d; ret+=d;
            if (ret==mn) break;
        }
    }
    return ret;
}
int dinic(int st,int ed) {
    int ret=0;
    while (bfs(st,ed)) {
        rep(i,st,ed) cur[i]=ls[i];
        ret+=find(st,ed,INF);
    }
    return ret;
}
int main(void) {
    int T=read();
    while (T--) {
        int n=read(),m=read();
        int cnt=0,mxr=0,tot=0; edCnt=1;
        fill(rec,0); fill(ls,0);
        rep(i,1,n) {
            c[i]=read();
            l[i]=read(),r[i]=read();
            rec[l[i]]=rec[r[i]]=1;
            mxr=std:: max(mxr,r[i]);
            tot+=c[i];
        }
        s[++cnt].l=1;
        rep(i,2,mxr)
            if (rec[i]) {
                s[cnt].r=i;
                s[cnt].len=s[cnt].r-s[cnt].l;
                s[++cnt].l=i;
            }
        cnt--;
        rep(i,1,n) {
            addEdge(0,i,c[i]);
            rep(j,1,cnt) {
                if (l[i]<=s[j].l&&r[i]>=s[j].r) {
                    addEdge(i,j+n,s[j].len);
                }
            }
        }
        rep(j,1,cnt) {
            addEdge(j+n,n+cnt+1,s[j].len*m);
        }
        int mxFlow=dinic(0,n+cnt+1);
        if (mxFlow==tot) puts("Yes");
        else puts("No");
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值