[有上下界最小流] BZOJ2502: 清理雪道

题意

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

题解

题目抽象出来就是用最少的路径数覆盖一个DAG,即每条边至少经过一次。
直接搞成有源汇有上下界的最小流即可,建图很简单:
S–>i 容量无限大,i–>T 容量无限大
i–>j 容量 [1,+]
然后就好了。

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1005,maxe=200005;
struct Edge{
    int from,to,flow,cap;
    Edge(int t1=0,int t2=0,int t3=0,int t4=0){ from=t1; to=t2; flow=t3; cap=t4; }
} Es[maxe];
int fir[maxn],nxt[maxe],tot=1;
void add(int x,int y,int z){
    Es[++tot]=Edge(x,y,0,z); nxt[tot]=fir[x]; fir[x]=tot;
    Es[++tot]=Edge(y,x,0,0); nxt[tot]=fir[y]; fir[y]=tot;
}
queue <int> que;
int S,T,SS,TT,N,d[maxn],tmp[maxn];
int n,ans0;
bool Bfs(int S,int T){
    memset(d,63,sizeof(d)); int INF=d[0];
    while(!que.empty()) que.pop(); 
    que.push(S); d[S]=0; 
    while(!que.empty()){
        int x=que.front(); que.pop();
        for(int j=fir[x];j;j=nxt[j]) if(d[Es[j].to]==INF&&Es[j].cap>Es[j].flow){
            d[Es[j].to]=d[x]+1; que.push(Es[j].to);
        }
    }
    return d[T]!=INF;
}
int pos[maxn];
int find_Dfs(int x,int flow,int T){
    if(x==T||flow==0) return flow;
    int res=0,t;
    for(int &j=pos[x];j;j=nxt[j]){
        if(d[x]+1==d[Es[j].to]&&(t=find_Dfs(Es[j].to,min(flow,Es[j].cap-Es[j].flow),T))>0){
            Es[j].flow+=t; Es[j^1].flow-=t; 
            res+=t; flow-=t; if(flow==0) break;
        }
    }
    return res;
}
int Dinic(int S,int T){
    int MaxFlow=0;
    while(Bfs(S,T)){
        for(int i=1;i<=N;i++) pos[i]=fir[i]; 
        MaxFlow+=find_Dfs(S,1e+9,T);
    }
    return MaxFlow;
}
void Del(int x){
    for(int j=fir[x];j;j=nxt[j]) Es[j].flow=Es[j].cap=Es[j^1].flow=Es[j^1].cap=0;
} 
int main(){
    freopen("bzoj2502.in","r",stdin);
    freopen("bzoj2502.out","w",stdout);
    scanf("%d",&n);
    N=n; S=++N; T=++N; SS=++N; TT=++N;
    for(int i=1;i<=n;i++){
        int t,x; scanf("%d",&t);
        while(t--){
            scanf("%d",&x);
            add(i,x,1e+9); tmp[i]--; tmp[x]++;
        }
    }
    for(int i=1;i<=n;i++) add(S,i,1e+9), add(i,T,1e+9);
    for(int i=1;i<=n;i++){
        if(tmp[i]>0) add(SS,i,tmp[i]);
                else add(i,TT,-tmp[i]);
    }
    add(T,S,1e+9);
    Dinic(SS,TT); ans0=Es[tot-1].flow+0;
    Es[tot].cap=Es[tot].flow=Es[tot-1].cap=Es[tot-1].flow=0; Del(SS); Del(TT);
    printf("%d\n",ans0-Dinic(T,S));
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值