您的位置:首页 > 理论基础 > 计算机网络

POJ 2391 Ombrophobic Bovines (二分,最短路径,网络流sap,dinic,预留推进 )

2014-06-05 23:15 501 查看
Ombrophobic Bovines

Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 14019Accepted: 3068
Description

FJ's cows really hate getting wet so much that the mere thought of getting caught in the rain makes them shake in their hooves. They have decided to put a rain siren on the farm to let them know when rain is approaching. They intend to create a rain evacuation
plan so that all the cows can get to shelter before the rain begins. Weather forecasting is not always correct, though. In order to minimize false alarms, they want to sound the siren as late as possible while still giving enough time for all the cows to get
to some shelter.

The farm has F (1 <= F <= 200) fields on which the cows graze. A set of P (1 <= P <= 1500) paths connects them. The paths are wide, so that any number of cows can traverse a path in either direction.

Some of the farm's fields have rain shelters under which the cows can shield themselves. These shelters are of limited size, so a single shelter might not be able to hold all the cows. Fields are small compared to the paths and require no time for cows to traverse.

Compute the minimum amount of time before rain starts that the siren must be sounded so that every cow can get to some shelter.
Input

* Line 1: Two space-separated integers: F and P

* Lines 2..F+1: Two space-separated integers that describe a field. The first integer (range: 0..1000) is the number of cows in that field. The second integer (range: 0..1000) is the number of cows the shelter in that field can hold. Line i+1 describes field
i.

* Lines F+2..F+P+1: Three space-separated integers that describe a path. The first and second integers (both range 1..F) tell the fields connected by the path. The third integer (range: 1..1,000,000,000) is how long any cow takes to traverse it.
Output

* Line 1: The minimum amount of time required for all cows to get under a shelter, presuming they plan their routes optimally. If it not possible for the all the cows to get under a shelter, output "-1".
Sample Input
3 4
7 2
0 4
2 6
1 2 40
3 2 70
2 3 90
1 3 120

Sample Output
110

Hint

OUTPUT DETAILS:

In 110 time units, two cows from field 1 can get under the shelter in that field, four cows from field 1 can get under the shelter in field 2, and one cow can get to field 3 and join the cows from that field under the shelter in field 3. Although there are
other plans that will get all the cows under a shelter, none will do it in fewer than 110 time units.
Source

USACO 2005 March Gold

题目大意:

有n头奶牛及牛棚,以及m条边,接下来告诉你n行,每行表示这个牛棚奶牛实际数目,以及能容纳的数目,接下来m行告诉你奶牛从一个牛棚到另一个牛棚所需要的时间,问你,是否所有奶牛能够到达牛棚,如果不能,输出-1,如果能,输出最短时间。

解题思路:

这种最短时间,想到了二分,是否能到达,想到了最短路径,是否能全部容纳,想到了构建一张网络图,来解决。

这题采用了三种网络流解法,sap效率最高,用了300多毫秒,dinic用了700多毫秒,而预留推进,我最喜欢用的,则超时了



代码一:sap效率最高,用了300多毫秒

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

#define INF 2000000000
#define N 100010
typedef long long ll;

const int maxn=1100;
struct edge{
    int u,v,next,cap;
}e[maxn*maxn];
int n,head
,tol,top,st
;
int src,des,dep
,gap
;

void adde(int u,int v,int c){
    e[tol].u=u,e[tol].v=v,e[tol].next=head[u],e[tol].cap=c,head[u]=tol++;
    e[tol].u=v,e[tol].v=u,e[tol].next=head[v],e[tol].cap=0,head[v]=tol++;
}

void bfs(){//对于反边计算层次
    for(int i=0;i<N;i++) dep[i]=N-1;
    memset(gap,0,sizeof gap);
    gap[0]=1,dep[des]=0;
    int q
,l=0,r=0,u,v;
    q[r++]=des;
    while(l!=r){
        u=q[l++];
        l=l%N;
        for(int i=head[u];i!=-1;i=e[i].next){
            v=e[i].v;
            if(e[i].cap!=0||dep[v]!=N-1) continue;
            q[r++]=v;
            r=r%N;
            ++gap[dep[v]=dep[u]+1];
        }
    }
}

int sap(){
    bfs();
    int u=src,s
,top=0,res=0,ii;
    int cur
;
    memcpy(cur,head,sizeof head);
    while(dep[src]<n){
        if(u==des){//求得一条增广路
           int minf=INF,pos=n;
           for(int i=0;i<top;i++){
              if(minf>e[s[i]].cap){
                  minf=e[s[i]].cap;
                  pos=i;
              }
           }
           for(int i=0;i<top;i++){
              e[s[i]].cap-=minf;
              e[s[i]^1].cap+=minf;
           }
           top=pos;
           res+=minf;
           u=e[s[top]].u;//优化1
        }
        if(dep[u]!=0&&gap[dep[u]-1]==0) break;//出现断层
        ii=-1;
        for(int i=cur[u];i!=-1;i=e[i].next){
             if(dep[e[i].v]==N-1) continue;
             if(e[i].cap!=0&&dep[u]==dep[e[i].v]+1){ii=i;break;}
        }
        if(ii!=-1){//有允许弧
            cur[u]=ii;
            s[top++]=ii;
            u=e[ii].v;
        }else{//不断回退找增光路
            int mind=n;
            for(int i=head[u];i!=-1;i=e[i].next){
                if(e[i].cap==0) continue;
                if(dep[e[i].v]<mind) mind=dep[e[i].v],cur[u]=i;
            }
            --gap[dep[u]];
            ++gap[dep[u]=mind+1];//优化2
            if(u!=src) u=e[s[--top]].u;
        }
    }
    return res;
}

const ll inf=1e18;
int nn,m,now[maxn],need[maxn],sum;
ll dis[maxn][maxn],maxr;

void initial(){
    sum=0;
    for(int i=0;i<=nn;i++){
        for(int j=0;j<=nn;j++){
            dis[i][j]=inf;
        }
        dis[i][i]=0;
    }
}

void input(){
    for(int i=1;i<=nn;i++){
        scanf("%d%d",&now[i],&need[i]);
        sum+=now[i];
    }
    for(int i=0;i<m;i++){
        int u,v,x;
        scanf("%d%d%d",&u,&v,&x);
        maxr+=x;
        if(x<dis[u][v]){
            dis[u][v]=dis[v][u]=x;
        }
    }
    for(int k=1;k<=nn;k++){
        for(int i=1;i<=nn;i++){
            for(int j=1;j<=nn;j++){
                if(dis[i][k]+dis[k][j]<dis[i][j]) dis[i][j]=dis[i][k]+dis[k][j];
            }
        }
    }
    maxr=0;
    for(int i=1;i<=nn;i++){
        for(int j=1;j<=nn;j++){
            if(dis[i][j]!=inf && dis[i][j]>maxr) maxr=dis[i][j];
        }
    }
}

void build(ll c){
	tol=0;
    memset(head,-1,sizeof head);
	src=1,des=2*nn+2,n=2*nn+2;
	for(int i=1;i<=nn;i++){
        adde(src,i+1,now[i]);
        adde(i+nn+1,des,need[i]);
	}
	for(int i=1;i<=nn;i++){
        for(int j=1;j<=nn;j++){
            if(dis[i][j]<=c) adde(i+1,j+nn+1,INF);
        }
	}
}

void solve(){
    ll l=0,r=maxr;
    build(r);
    if(sap()<sum){
        printf("-1\n");
        return;
    }
    while(l<r){
        ll mid=(l+r)/2;
        build(mid);
        if(sap()>=sum) r=mid;
        else l=mid+1;
    }
    cout<<r<<endl;
}

int main(){
    scanf("%d%d",&nn,&m);
    initial();
    input();
    solve();
    return 0;
}


代码二:dinic效率其次,用了300多毫秒

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

const int INF=1000000000;
const int maxn=500,maxm=500000;

struct edge{
    int u,v,f,next;
}e[maxm*2+10];

int n,src,sink,cnt,head[maxn+10];

void adde(int u,int v,int f){
	e[cnt].u=u,e[cnt].v=v,e[cnt].f=f,e[cnt].next=head[u],head[u]=cnt++;
	e[cnt].u=v,e[cnt].v=u,e[cnt].f=0,e[cnt].next=head[v],head[v]=cnt++;
}

void init(){
    memset(head,-1,sizeof(head));
    cnt=0;
}

queue <int> q;
bool visited[maxn+10];
int dist[maxn+10];

void bfs(){
    memset(dist,0,sizeof(dist));
    while(!q.empty()) q.pop();
    visited[src]=true;
    q.push(src);
    while(!q.empty()){
        int s=q.front();
        q.pop();
        for(int i=head[s];i!=-1;i=e[i].next){
            int d=e[i].v;
            if(e[i].f>0 && !visited[d]){
                q.push(d);
                dist[d]=dist[s]+1;
                visited[d]=true;
            }
        }
    }
}

int dfs(int u,int delta){
    if(u==sink) return delta;
    else{
        int ret=0;
        for(int i=head[u];delta && i!=-1;i=e[i].next){
            if(e[i].f>0 && dist[e[i].v]==dist[u]+1){
                int d=dfs(e[i].v,min(e[i].f,delta));
                e[i].f-=d;
                e[i^1].f+=d;
                delta-=d;
                ret+=d;
            }
        }
        return ret;
    }
}

int maxflow(){
    int ret=0;
    while(true){
        memset(visited,false,sizeof(visited));
        bfs();
        if(!visited[sink]) return ret;
        ret+=dfs(src,INF);
    }
    return ret;
}

typedef long long ll;
const ll inf=2e18;
int nn,m,now[maxn],need[maxn],sum;
ll dis[maxn][maxn],maxr;

void initial(){
    sum=0;
    for(int i=0;i<=nn;i++){
        for(int j=0;j<=nn;j++){
           if(i!=j) dis[i][j]=inf;
           else dis[i][j]=0;
        }
    }
}

void input(){
    for(int i=1;i<=nn;i++){
        scanf("%d%d",&now[i],&need[i]);
        sum+=now[i];
    }
    for(int i=0;i<m;i++){
        int u,v,x;
        scanf("%d%d%d",&u,&v,&x);
        if(x<dis[u][v]){
            dis[u][v]=dis[v][u]=x;
        }
    }
    for(int k=1;k<=nn;k++){
        for(int i=1;i<=nn;i++){
            for(int j=1;j<=nn;j++){
                if(dis[i][k]+dis[k][j]<dis[i][j]) dis[i][j]=dis[i][k]+dis[k][j];
            }
        }
    }
    maxr=0;
    for(int i=1;i<=nn;i++){
        for(int j=1;j<=nn;j++){
            if(dis[i][j]!=inf && dis[i][j]>maxr) maxr=dis[i][j];
        }
    }
}

void build(ll c){
	init();
	src=1,sink=2*nn+2,n=2*nn+2;
	for(int i=1;i<=nn;i++){
        adde(src,i+1,now[i]);
        adde(i+nn+1,sink,need[i]);
	}
	for(int i=1;i<=nn;i++){
        for(int j=1;j<=nn;j++){
            if(dis[i][j]<=c) adde(i+1,j+nn+1,INF);
        }
	}
}

void solve(){
    ll l=0,r=maxr;
    build(r);
    if(maxflow()<sum){
        printf("-1\n");
        return;
    }
    while(l<r){
        ll mid=(l+r)/2;
        build(mid);
        if(maxflow()>=sum) r=mid;
        else l=mid+1;
    }
    cout<<r<<endl;
}

int main(){
    scanf("%d%d",&nn,&m);
    initial();
    input();
    solve();
    return 0;
}


代码三:预留推进,最慢,超时了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

typedef long long ll;

const ll inf=1e18;
const int maxn=1100;
struct edge{
	int u,v,next,f;
	edge(int u0=0,int v0=0,int f0=0,int next0=0){
		u=u0,v=v0,f=f0,next=next0;
	}
}e[maxn*maxn];
int head[maxn*2],visited[maxn*2],path[maxn*2];
int cnt,from,to,marked;
int n,m,now[maxn],need[maxn],sum;
ll dis[maxn][maxn],maxr;

void initial(){
    sum=0;
    for(int i=0;i<=n;i++){
        for(int j=0;j<=n;j++){
            dis[i][j]=inf;
        }
        dis[i][i]=0;
    }
}

void input(){
    for(int i=1;i<=n;i++){
        scanf("%d%d",&now[i],&need[i]);
        sum+=now[i];
    }
    for(int i=0;i<m;i++){
        int u,v;
        ll x;
        scanf("%d%d%lld",&u,&v,&x);
        maxr+=x;
        if(x<dis[u][v]){
            dis[u][v]=dis[v][u]=x;
        }
    }
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(dis[i][k]+dis[k][j]<dis[i][j]) dis[i][j]=dis[i][k]+dis[k][j];
            }
        }
    }
}

void adde(int u,int v,int f){
	e[cnt].u=u,e[cnt].v=v,e[cnt].f=f,e[cnt].next=head[u],head[u]=cnt++;
	e[cnt].u=v,e[cnt].v=u,e[cnt].f=0,e[cnt].next=head[v],head[v]=cnt++;
}

bool bfs(){
    int s=from,d;
    queue <int> q;
    q.push(s);
    marked++;
    visited[s]=marked;
    while(!q.empty()){
        s=q.front();
        q.pop();
        for(int i=head[s];i!=-1;i=e[i].next){
            d=e[i].v;
            if(visited[d]!=marked && e[i].f>0){
                visited[d]=marked;
                path[d]=i;
                q.push(d);
                if(d==to) return true;
            }
        }
    }
    return false;
}

int maxf(){
    int maxflow=0;
    while(bfs() ){
        int flow=maxn;
     	for(int i=to;i!=from;i=e[path[i]].u){
     		flow=min(e[path[i]].f,flow);
        }
        for(int i=to;i!=from;i=e[path[i]].u){
     		e[path[i]].f-=flow;
         	e[path[i]^1].f+=flow;
        }
        maxflow+=flow;
     }
     return maxflow;
}

void build(ll c){
    cnt=0;
	marked=1;
	memset(head,-1,sizeof(head));
	memset(visited,0,sizeof(visited));
	from=0,to=2*n+1;
	for(int i=1;i<=n;i++){
        adde(from,i,now[i]);
        adde(i+n,to,need[i]);
	}
	for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(dis[i][j]<=c) adde(i,j+n,(1<<30));
        }
	}
	maxr=0;
	for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(dis[i][j]!=inf && dis[i][j]>maxr) maxr=dis[i][j];
        }
    }
}

void solve(){
    ll l=0,r=maxr;
    build(r);
    if(maxf()<sum){
        printf("-1\n");
        return;
    }
    while(l<r){
        ll mid=(l+r)/2;
        build(mid);
        if(maxf()>=sum) r=mid;
        else l=mid+1;
    }
    cout<<r<<endl;
}

int main(){
    scanf("%d%d",&n,&m);
    initial();
    input();
    solve();
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: