Monkeys in the Emei Mountain UVA - 11167

本文介绍了一道经典的网络流问题——UVA-11167猴子喝水问题。该问题涉及最大流算法、离散化及输出方案等知识点,并通过实例详细解析了如何建立模型和求解过程。

Monkeys in the Emei Mountain UVA - 11167

网络流·最大流·离散化·输出方案

很有价值的一道网络流

题目大意:

题目大概说有n只猴子(LJM),猴子们在某个时间段[li,ri]需要喝vi的水,猴子在一个时间单位里能喝一单位的水。各个单位时间段最多允许m只猴子同时喝水,问猴子们能否成功喝水并输出一个可行的方案,输出方案的时间段区间要从小到大排序并且合并连续的区间。
猴子数n<=100,l,r,v<=50000

题解:

非常直观的模型:
S -> monkey i vi
monkey i -> time j 1 (j∈[li,ri])
time j -> T m

但是时间节点很多,会超时,怎么办呢?
猴子很少啊,换言之,有用的时间节点很少。
因此可以把时间离散化,让一个时间节点代表一段时间。
建图如下:
S -> monkey i vi
monkey i -> time j Rj-Lj ([Lj,Rj]包含于[li,ri])
time j -> T m*(Rj-Lj)
跑最大流,检验流量是否是 Σvi

如何寻找一个方案呢?
首先找到monkey x 连向time t 的流量f>0的边,
这说明x在t代表的这个时间内喝了f的水。
令used[t][k]表示时间段t的第k个喝水位置已经用到了used[t][k]的时间点。
我们在给猴子找位置的时候从小到大枚举k,贪心的先用编号小的位置,把前面的尽量填满再用后面的位置。这个根据used计算并且更新used即可。
而且这样保证了一只猴子不会在同一时刻用两个位置。
最后把给猴子选好的时间段排个序,合并一下。
详情见代码。

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <algorithm>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
#define MP make_pair
#define fir first
#define sec second
using namespace std;
typedef pair<int,int> pii;

const int N = 1005;
const int M = 2e5+5;
const int INF = 0x3f3f3f3f;

int n,m,S,T,tot;
int tp[N],tpcnt;
int sz[N],used[N][105];
pii ans[N][105];

struct Node{
    int l,r,v;
}monkey[N];

struct Edge{
    int to,next,flow,cap;
}e[M];
int head[N], ec=1;
void clear(){ memset(head,0,sizeof(head)); ec=1; }
void add(int a,int b,int c){
    ec++; e[ec].to=b; e[ec].cap=c; e[ec].flow=0;
    e[ec].next=head[a]; head[a]=ec;
}
void add2(int a,int b,int c){
//  D(a); D(b); D(c); E;
    add(a,b,c); add(b,a,0);
}

bool vis[N]; int d[N];
bool bfs(){
    memset(vis,false,sizeof(vis));
    queue<int> q; q.push(S); d[S]=0; vis[S]=true;
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(e[i].cap>e[i].flow && !vis[v]){
                vis[v]=true; d[v]=d[u]+1;
                if(v==T) return true;
                q.push(v);
            }
        }
    } 
    return false;
}

int dfs(int u,int a){
    if(u==T || a==0) return a;
    int flow=0,f;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(d[v]==d[u]+1 && (f=dfs(v,min(a,e[i].cap-e[i].flow)))){
            flow+=f; a-=f;
            e[i].flow+=f; e[i^1].flow-=f;
            if(a==0) break;
        }
    }
    if(a) d[u]=-1;
    return flow;
}

int mxf(){
    int flow=0;
    while(bfs()){
        flow+=dfs(S,INF);
    }
    return flow;
}

void build(){
    clear(); S=0; T=n+tpcnt+1;
    for(int i=1;i<=n;i++) 
        add2(S,i,monkey[i].v);
    for(int i=1;i<tpcnt;i++) 
        add2(i+n,T,m*(tp[i]-tp[i-1]));
    for(int i=1;i<=n;i++)
        for(int j=1;j<tpcnt;j++)
            if(monkey[i].l<=tp[j-1] && monkey[i].r>=tp[j])
                add2(i,j+n,tp[j]-tp[j-1]);
}

void work(){
    int flow=mxf();
    if(flow<tot){
        puts("No"); return;
    }
    puts("Yes");
    for(int x=1;x<=n;x++){
//      D(x); E;
        for(int i=head[x];i;i=e[i].next){
            if(!e[i].flow || e[i].cap==0) continue;
            int f=e[i].flow;
            int t=e[i].to-n-1;
            for(int k=1;k<=m;k++){
                if(used[t][k]==tp[t+1]-tp[t]) continue;
                if(used[t][k]+f<=tp[t+1]-tp[t]){
                    ans[x][++sz[x]]=MP(tp[t]+used[t][k],tp[t]+used[t][k]+f);
                    used[t][k]+=f;
                    break;
                }
                else{
                    f-=(tp[t+1]-tp[t]-used[t][k]);
                    ans[x][++sz[x]]=MP(tp[t]+used[t][k],tp[t+1]);
                    used[t][k]=tp[t+1]-tp[t];
                    if(!f) break;
                }
            }
        }
        sort(ans[x]+1,ans[x]+1+sz[x]);
//      for(int i=1;i<=sz[x];i++){
//          printf(" (%d,%d)",ans[x][i].fir,ans[x][i].sec);
//      }
//      E;
        ans[x][sz[x]+1]=MP(-1,-1);
    }
    for(int x=1;x<=n;x++){
//      D(x); E;
        int tp=0, a=1,b=1;
        while(a<=sz[x]){
            while(b<sz[x] && ans[x][b].sec==ans[x][b+1].fir) b++;
//          D(a); D(b); E;
            ans[x][++tp]=MP(ans[x][a].fir,ans[x][b].sec);
            a=b=b+1;
        }
        sz[x]=tp;
    }
    for(int x=1;x<=n;x++){
        printf("%d",sz[x]);
        for(int j=1;j<=sz[x];j++){
            printf(" (%d,%d)",ans[x][j].fir,ans[x][j].sec);
        }
        puts("");
    }
}

int main(){
    freopen("a.in","r",stdin);
    int cas=0;
    while(~scanf("%d",&n) && n){
        scanf("%d",&m);
        tot=0; tpcnt=0;
        memset(sz,0,sizeof(sz));
        memset(used,0,sizeof(used));
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",&monkey[i].v,&monkey[i].l,&monkey[i].r);
            tp[tpcnt++]=monkey[i].l; tp[tpcnt++]=monkey[i].r;
            tot+=monkey[i].v;
        }
        sort(tp,tp+tpcnt);
        tpcnt=unique(tp,tp+tpcnt)-tp;
        printf("Case %d: ",++cas);
//      for(int i=0;i<tpcnt;i++) D(tp[i]); E;
        build();
        work();
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值