poj 2391 最大流+floyd+二分(转移避难)

来源:互联网 发布:淘宝店铺出售法律风险 编辑:程序博客网 时间:2024/05/09 18:37

题意:无向图中给出n个点,m条边,每个点给定两个值a和b,a表示当前在该点的人数,b表示该点的人数限制。之后给定m条边以及边上的权值。问是否所有人都能通过转移找到避难处,如果能,输出最短的移动距离。

思路:先用floyd求出任意两点之间的最短距离,然后二分答案。对每个二分值建立网络流进行求解(整个思路类似2112挤奶那道题)。建图时要拆点。对每个点x,拆成x'和x'',然后x'和x''之间有一条无限容量的边,这样的话,随便多少牛路过这个点都是可以的,如果i->j这条边符合了距离限制,就加i'->j'' 所有的边加完后,建立源点,对所有的x'连边,容量为已经有的牛,汇点的话,就用所有的j''连接汇点,容量就是能容纳的牛的数量。

最大流用dinic的递归写法,还是比用栈写简单不少。

#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <queue>#include <cstdlib>using namespace std;#define clc(s,t) memset(s,t,sizeof(s))#define INF 0x3fffffffffffffff#define N 205int a[N],b[N];long long dis[N][N],low,high,mid;int n,m;int f[N*2],first[N*2],top,d[N*2];struct edge{    int y,c,next;}e[2*(N*N+2*N+5)];void init(){    int i,j;    for(i = 1;i<=n;i++)        for(j = 1;j<=n;j++)            dis[i][j] = INF;    low = high = 0;}void add(int x,int y,int c){    e[top].y = y;    e[top].c = c;    e[top].next = first[x];    first[x] = top++;}void floyd(){    int i,j,k;    for(k = 1;k<=n;k++)        for(i = 1;i<=n;i++)            for(j = 1;j<=n;j++){                dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);                if(k==n && dis[i][j] != INF)                    high = max(high,dis[i][j]);            }}void create(long long x){    int i,j;    clc(first,-1);    top = 0;    for(i = 1;i<=n;i++){        add(0,i,a[i]);        add(i,0,0);        add(n+i,2*n+1,b[i]);        add(2*n+1,n+i,0);        add(i,n+i,1005);        add(n+i,i,0);    }    for(i = 1;i<=n;i++)        for(j = 1;j<=n;j++)            if(dis[i][j] <= x){                add(i,n+j,1005);                add(n+j,i,0);            }}int bfs(int s,int t){    int i,now;    clc(d, -1);    queue<int>q;    q.push(s);    d[s] = 0;    while(!q.empty()){        now = q.front();        q.pop();        for(i = first[now];i!=-1;i=e[i].next)            if(-1 == d[e[i].y] && e[i].c>0){                d[e[i].y] = d[now]+1;                if(e[i].y == t)                    return 1;                q.push(e[i].y);            }    }    return 0;}int dfs(int now,int a){    int i,j,res=0;    if(now == 2*n+1 || a==0)        return a;    for(i = f[now];i!=-1;i = f[now] = e[i].next)        if(d[e[i].y] == d[now]+1 && (j = (dfs(e[i].y,min(a,e[i].c))))>0){            e[i].c -= j;            e[i^1].c += j;            res += j;            a -= j;            if(!a)                break;        }    return res;}int dinic(int s,int t){    int res=0;    while(bfs(s,t)){        memcpy(f, first, sizeof(first));        res += dfs(s,1005);    }    return res;}int main(){    int i,j,x,y,sum,flag;    long long w;    while(scanf("%d %d",&n,&m)!=EOF){        init();        flag = sum = 0;        for(i = 1;i<=n;i++){            scanf("%d %d",&a[i],&b[i]);            sum += a[i];        }        for(i = 1;i<=m;i++){            scanf("%d %d %lld",&x,&y,&w);            dis[x][y] = min(dis[x][y],w);            dis[y][x] = dis[x][y];        }        floyd();        high++;//没加这句贡献了一次WA,说明答案可能正是最长的路径值        while(low < high){            mid = (low+high)>>1;            create(mid);            j = dinic(0,2*n+1);            if(j==sum){                high = mid;                flag = 1;            }            else                low = mid+1;        }        if(flag)            printf("%lld\n",low);        else            printf("-1\n");    }    return 0;}


0 0
原创粉丝点击