URAL 1774 Barber of the Army of Mages 网络流

题目大意:有N个烙饼,一个平底锅最多可以一次放K张饼。一个饼正反面都需要烙,烙一个面至少需要一个单位的时间。第i个饼会第ti时间送到厨房,要是在si时间内还没有烙好,顾客就会投诉。问存不存在一种烙饼方案,使得所有饼都按时烙好。存在则输出Yes,并输出每个饼的下锅时间和出炉时间,不存在则输出No。

这题一乍一看还以为是贪心,遗憾的是WA了。听了题解顿悟是网络流。
建图是这样的,对于每一张饼,与它所有能够被烙的时间点连一条容量为1的边,并与汇点连一条容量为2的边(表示正反面各烙一单位时间),接着从源点向所有单位时间连一条容量为K的边,表示一个时刻锅里最多有K张饼(烙过一次的饼可以暂时离锅)。

(ISAP)

#include <bits/stdc++.h>
#define MAXN 3000
#define INIT 16843009
using namespace std;
const int S=2222,T=2333;
int f[MAXN][MAXN],gap[MAXN],pre[MAXN],d[MAXN],ct;
vector<int>p[MAXN];
typedef vector<int>::iterator iter;
queue<int>q;
void bfs(int s){
    memset(d,1,sizeof d);
    memset(gap,0,sizeof gap);
    d[s]=0;gap[0]=1;
    q.push(s);
    while(!q.empty()){
        int tmp=q.front();q.pop();++ct;
        for(iter it=p[tmp].begin();it!=p[tmp].end();it++){
            if(f[*it][tmp]>0 && d[*it]==INIT){
                d[*it]=d[tmp]+1;
                ++gap[d[*it]];
                q.push(*it);
            }
        }
    }
}
int f_next(int x){
    for(iter it=p[x].begin();it!=p[x].end();it++)
        if(f[x][*it]>0 && d[x]==d[*it]+1)
            return *it;
    return -1;
}
int isap(){
    bfs(T);
    int u=S,ans=0;
    memset(pre,-1,sizeof pre);
    while(d[u]<ct){
        int v=f_next(u);
        if(v>=0){
            pre[v]=u;u=v;
            if(u==T){
                int k=INIT;
                for(int i=u;i!=S;i=pre[i])
                    k=min(k,f[pre[i]][i]);
                for(int i=u;i!=S;i=pre[i])
                    f[pre[i]][i]-=k,f[i][pre[i]]+=k;
                ans+=k;
                u=S;
            }
        }else{
            if(--gap[d[u]]==0)return ans;
            int mind=T;
            for(iter it=p[u].begin();it!=p[u].end();it++)
                if(f[u][*it]>0)mind=min(mind,d[*it]+1);
            ++gap[mind];
            d[u]=mind;
            if(u!=S)u=pre[u];
        }
    }
    return ans;
}
//ISAP 
void add(int u,int v,int flow){
    p[u].push_back(v);
    p[v].push_back(u);
    f[u][v]=flow;
}
//加边 
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1,l,r;i<=n;i++){
        scanf("%d%d",&l,&r);
        r+=l-1;
        for(int j=l;j<=r;j++)
            add(j,i+2000,1);
            //每一张饼与所有能够被烙的时间点连一条容量为1的边
        add(i+2000,T,2);
        //每一张饼与汇点连一条容量为2的边
    }
    for(int i=0;i<=2000;i++)add(S,i,k);
    //从源点向所有单位时间连一条容量为K的边
    int mflow=isap();
    if(mflow!=2*n)printf("No\n");
    else{
        printf("Yes\n");
        for(int i=1;i<=n;i++){
            int answer[2],cnt=0;
            for(iter it=p[i+2000].begin();it!=p[i+2000].end();it++)
                if(f[i+2000][*it]==1)answer[cnt++]=*it;
            if(answer[0]>answer[1])swap(answer[0],answer[1]);
            printf("%d %d\n",answer[0],answer[1]);
        }
    }
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值