bzoj2502 清理雪道 上下界网络流

Description


滑雪场坐落在FJ省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。

2 <= n <= 100

Solution


考虑直接网络流要怎么搞。对于入度为0的点连源点,出度为0的点连汇点,斜坡就连费用为[1,INF]的边
注意到我们要求的是一个有上下界的最小流,然后我就学习了一波上下界网络流( ̄▽ ̄)”
(下面的各种流都是有上下界的网络流)

建图方法:
新建超级源点汇点SS和TT,对于原图中的一条边(x,y,[l,r])我们连(SS,y,l),(TT,x,l),(x,y,r-l)
若是有源汇的图就多连一条(T,S,INF),其余同上
存在可行流当且仅当SS的边全部满流
最小流可以二分答案mid,把S和T的连边变为(T,S,mid)跑可行流判断
最大流同理
最小费用流直接在边上加权,其他不变

Code


#include <stdio.h>
#include <string.h>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define copy(x,t) memcpy(x,t,sizeof(x))

const int INF=0x3f3f3f3f;
const int N=2005;
const int E=400005;

std:: queue <int> que;

struct edge {int x,y,w,next;} e[E];

int dis[N],d[N],S,T,SS,TT;
int cur[N],ls[N],edCnt=1;

void add_edge(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;
}

bool bfs(int st,int ed) {
    for (;!que.empty();) que.pop();
    que.push(st);
    fill(dis,0); dis[st]=1;
    for (;!que.empty();) {
        int now=que.front(); que.pop();
        for (int i=ls[now];i;i=e[i].next) {
            if (e[i].w>0&&!dis[e[i].y]) {
                dis[e[i].y]=dis[now]+1;
                if (e[i].y==ed) return true;
                que.push(e[i].y);
            }
        }
    }
    return false;
}

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(e[i].w,mn-ret));
            e[i].w-=d; e[i^1].w+=d; ret+=d;
            if (mn==ret) break;
        }
    }
    return ret;
}

int dinic(int st,int ed) {
    int ret=0;
    for (;bfs(st,ed);) {
        copy(cur,ls);
        ret+=find(st,ed,INF);
    }
    return ret;
}

bool check(int mid,int n) {
    for (int i=2;i<=edCnt;i+=2) {
        e[i].w+=e[i^1].w; e[i^1].w=0;
    }
    e[edCnt^1].w=mid;
    dinic(SS,TT);
    bool flag=true;
    for (int i=ls[SS];i;i=e[i].next) {
        if (e[i].w) flag=false;
    }
    return flag;
}

int main(void) {
    int n; scanf("%d",&n);
    S=0,T=n+1,SS=n+2,TT=n+3;
    rep(i,1,n) {
        int s; scanf("%d",&s);
        if (!s) add_edge(i,T,INF);
        for (;s--;) {
            int y; scanf("%d",&y); d[y]++;
            add_edge(SS,y,1); add_edge(i,TT,1);
            add_edge(i,y,INF-1);
        }
    }
    rep(i,1,n) if (!d[i]) {
        add_edge(S,i,INF);
    }
    add_edge(T,S,INF);
    int l=1,r=INF;
    while (l<=r) {
        int mid=(l+r)>>1;
        if (check(mid,n)) r=mid-1;
        else l=mid+1;
    }
    printf("%d\n", r+1);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/81088842