2011.07.22

    基于昨天未尽的探讨,这个样子的ISAP没有使用BFS寻找最短路,而是用了头几次DFS来进行推进标号,效率降低得不多,编程复杂度确有很大改善。

简明ISAP+GAP模板:

#include<cstdio>
#include<climits>
#include<cstring>
#define MAXE 1000
#define MAXV 500
int head[MAXV];
int E,V,pos;
int vd[MAXV],d[MAXV];
inline int min(int a,int b){return a<b?a:b;}
struct edge
{
    int v,f,next;
}e[MAXE*2];
void add(int u,int v,int c)
{
    e[pos].v=v;
    e[pos].f=c;
    e[pos].next=head[u];
    head[u]=pos++;
}
int dfs(int u, int flow)
{
   int ret,v,tmp;
   if(u == V)return flow;          //找到增广路
   ret = 0;                   //min_d:u在残量网络里相通的边的最小标号距离,初始不能变为maxint
   for(int i=head[u];i!=-1;i=e[i].next) if(e[i].f>0 && d[u] == d[e[i].v] + 1)        //如果是允许弧
   {
      tmp = dfs(e[i].v,min(flow-ret,e[i].f));      //如果找到,增广
      e[i].f -= tmp;
      e[i+1].f += tmp;
                                //由于存图方法保证逆流在顺流的下一个位置
      ret += tmp;
      if(ret == flow)return ret;
   }

   if(d[1] >= V) return ret;         //如果源点的标号距离大于n,即不存在增广路,这一步放在重标号之前
   vd[d[u]]--;
   if(vd[d[u]] == 0)d[1] = V;        //如果该标号距离的顶点是唯一的,那么删除后图出现断层
   d[u]++;
   vd[d[u]]++;
   return ret;
}

int main()
{
    freopen("in","r",stdin);
   int i,u,v,c,flow,ans = 0;
   while(scanf("%d%d",&E,&V)!=EOF){
       pos=1;
       memset(head,-1,sizeof(head));
       memset(d,0,sizeof(d));
       memset(vd,0,sizeof(vd));
        for(i=1;i<=E;i++){
            scanf("%d%d%d",&u,&v,&c);
            add(u,v,c);
            add(v,u,0);
        }
        vd[0] = V;
        while(d[1] < V){
            flow = dfs(1,INT_MAX);
            ans += flow;
        }
        printf("%d\n",ans);
        ans=0;
    }
    return 0;
}
      昨天那个算法验证过是正确的ISAP,不过这个没有BFS优化,用来过时间不严格的题目吧~毕竟那么短……

     今天改成了邻接表存图,应付大数据。

    至此,最大流算法已化归至dfs的简明优美的形式。



Relabel-to-Front算法

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

const int maxn=1024,OO=1000000000;
struct edge{
	int x,y;//两个顶点 
	int w;//容量 
	int f;
	edge *next,*back;
	edge(){}
	edge(int x,int y,int w,edge* next):x(x),y(y),w(w),f(0),next(next),back(0){}
	void* operator new(size_t, void* p){
		return p;
	}
}*E[maxn],*cur[maxn];

int H[maxn];//高度函数
int P[maxn];//preflow
int N,S,T;
void input(){
	int m;
	scanf("%d%d",&m,&N);
	memset(E,0,sizeof(E));
	edge* data=new edge[2*m];
	while(m--){
		int x,y,w;
		scanf("%d%d%d",&x,&y,&w);
		E[x]=new((void*)data++) edge(x,y,w,E[x]);
		E[y]=new((void*)data++) edge(y,x,0,E[y]);
		E[x]->back = E[y];
		E[y]->back = E[x];
	}
	S=1;
	T=N;
}
template <class T>
const T& min(const T& a,const T& b){
	return a<b?a:b;
}
void push(edge* e){
	int u=e->x,v=e->y;
	int delta=min(P[u],e->w);
	e->w -= delta;
	e->back->w += delta;
	e->f += delta;
	e->back->f = -(e->f);
	P[u]-=delta;
	P[v]+=delta;
}
void relabel(int u){
	int minlabel=OO;
	for(edge* e=E[u];e;e=e->next){
		if(e->w==0)continue;
		int v=e->y;
		if(H[v]<minlabel){
			minlabel=H[v];
			cur[u]=e;
		}
	}
	H[u]=minlabel+1;
}
void discharge(int u){
	while(P[u]>0){
		if(!cur[u]){
			relabel(u);
		}
		int v=cur[u]->y;
		if(cur[u]->w >0 && H[u]==H[v]+1)
			push(cur[u]);
		cur[u]=cur[u]->next;
	}
}
void init_preflow(){
	memset(H,0,sizeof(H));
	memset(P,0,sizeof(P));
	H[S]=N;
	P[S]=OO;
	for(edge* e=E[S];e;e=e->next)
		push(e);
}
struct vertex{
	int x;
	vertex* next;
	vertex(int x,vertex* next):x(x),next(next){}
	vertex(){}
	void* operator new(size_t, void* p){
		return p;
	}
};
vertex* build_list(){
	vertex* data=new vertex[N-2];
	vertex* L=0;
	for(int i=N;i>=1;--i)
		if(i!=S&&i!=T)
			L=new((void*)data++) vertex(i,L);
	return L;
}
int maxflow(){
	memcpy(cur,E,sizeof(E));
	init_preflow();
	vertex* L=build_list();//list
	vertex *v=L,*pre=0;
	while(v){
		int old_height=H[v->x];
		discharge(v->x);
		if(H[v->x]>old_height){
			if(pre){
				pre->next = v->next;
				v->next = L;
				L = v;
			}
			else;//本来就在表头 
		}
		pre = v;
		v = v->next;
	}
	return P[T];
}
int main(){
	freopen("ditch.in","r",stdin);
	freopen("ditch.out","w",stdout);
	
	input();
	int res=maxflow();
	printf("%d\n",res);
}

    嗯~学长说我们不是酱油队的话就必须不是酱油队啊~

    加了个油~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值