1459(3)

/*
邻接表实现ISAP

此题加了多个优化 间隙优化 ,current弧优化, 还有返回到0边优化

终极79ms了,改日再优化
*/


// include file
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <ctime>

#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <bitset>

#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <list>
#include <functional>

using namespace std;

// typedef
typedef long long LL;
typedef unsigned long long ULL;
typedef __int64 Bint;

// 
#define read freopen("in.txt","r",stdin)
#define write freopen("out.txt","w",stdout)
#define FORi(a,b,c) for(int i=(a);i<(b);i+=c)
#define FORj(a,b,c) for(int j=(a);j<(b);j+=c)
#define FORk(a,b,c) for(int k=(a);k<(b);k+=c)
#define FORp(a,b,c) for(int p=(a);p<(b);p+=c)
#define FORii(a,b,c) for(int ii=(a);ii<(b);ii+=c)
#define FORjj(a,b,c) for(int jj=(a);jj<(b);jj+=c)
#define FORkk(a,b,c) for(int kk=(a);kk<(b);kk+=c)

#define FF(i,a)    for(int i=0;i<(a);i++)
#define FFD(i,a)   for(int i=(a)-1;i>=0;i--)

#define Z(a) (a<<1)
#define Y(a) (a>>1)

const double eps = 1e-6;
const double INFf = 1e10;
const int INFi = 1000000000;
const double Pi = acos(-1.0);

template<class T> inline T sqr(T a){return a*a;}
template<class T> inline T TMAX(T x,T y)
{
	if(x>y) return x;
	return y;
}
template<class T> inline T TMIN(T x,T y)
{
	if(x<y) return x;
	return y;
}
template<class T> inline void SWAP(T &x,T &y)
{
	T t = x;
	x = y;
	y = t;
}
template<class T> inline T MMAX(T x,T y,T z)
{
	return TMAX(TMAX(x,y),z);
}


// code begin

#define MAXN 110
#define MAXM 21000
int N,NP,NC,M;
int source,sink;
struct node1
{
	int e;
	int next;
	int residual;
};

node1 mem[MAXM];
int G[MAXN];    // 正邻接表,正边和反向边在内存上差一 
node1 Rmem[MAXM]; 
int RG[MAXN];   // 反邻接表,这样我们就能快速的知道正边和反边的地理位置了

int dx;
int rdx;

int dis[MAXN];       // 到汇点的距离

int stk[MAXN],top;   // 其实就是个栈嘛
int stkmin[MAXN],mtop; //到当前为止的最小值
int stkdx[MAXN],dtop;  //记录最小值对应索引值
int stkver[MAXN],vtop; //记录最小值对应的顶点

int father[MAXN];    // 记录每个顶点的父亲
int curpos[MAXN];    // 每个点需要开始的位置
int laynum[MAXN];    // 每层的顶点个数
int que[MAXN];       // BFS用

void Add_edge(int a,int b,int c)
{
	// 居然有重边
	
	//if(dx>15000) printf("%d\n",dx);
	mem[dx].e = b;
	mem[dx].next = G[a];
	mem[dx].residual = c;
	G[a] = dx++;

	//还要反向边也要加进去
	//if(dx>15000) printf("%d\n",dx);
	mem[dx].e = a;
	mem[dx].next = G[b];
	mem[dx].residual = 0;
	G[b] = dx++;

	//if(rdx>15000) printf("%d\n",rdx);
	Rmem[rdx].e = a;
	Rmem[rdx].next = RG[b];
	Rmem[rdx].residual = c;
	RG[b] = rdx++;

	//if(rdx>15000) printf("%d\n",rdx);
	Rmem[rdx].e = b;
	Rmem[rdx].next = RG[a];
	Rmem[rdx].residual = 0;
	RG[a] = rdx++;

}

void BFS() // 从汇点出发
{
	int head(0),tail(0);
	memset(laynum,0,sizeof(laynum));

	FORi(1,N+1,1)
	{
		dis[i] = N;
		laynum[ dis[i] ] ++;
	}

	// 从汇点开始
	laynum[ dis[sink] ] --;
	dis[ sink ] = 0;
	laynum[ dis[sink] ] ++;

	que[++tail] = sink;
	int mdx;
	
	while(head!=tail)
	{
		int cur = que[++head];

		mdx = RG[cur];
		while(mdx!=-1)
		{
			int v = Rmem[mdx].e;
			if( dis[v]==N && Rmem[mdx].residual!=0 )
			{
				que[++tail] = v;

				laynum[N] --;
				dis[v] = dis[cur]+1;
				laynum[ dis[v] ]++;
			}

			mdx = Rmem[mdx].next;
		}
	}

}

int Augment()
{
	int minp = stkmin[mtop-1];

	// 找到做小边后,然后修改路径了
	FORi(0,top,1)
	{
		mem[stk[i]].residual -= minp;
		mem[(stk[i]&1)?(stk[i]-1):(stk[i]+1)].residual += minp;
		stkmin[i+1] -= minp;
	}

	return minp;
}

int Retreat(int &cur)
{
	// 找出所有儿子的最小值
	int tmp;
	int mind(N-1);
	int mdx = G[cur];
	while(mdx!=-1)
	{
		if( mem[mdx].residual>0 && dis[ mem[mdx].e ]<mind)
			mind = dis[ mem[mdx].e ];
		mdx = mem[mdx].next;
	}

	tmp = dis[cur];

	// relabel
	laynum[ dis[cur] ]--;
	dis[cur] = 1+mind;
	laynum[ dis[cur] ]++;
	
	//backtrack
	if(cur!=source) 
	{
		cur = father[cur];
		top --;
		mtop--;
		dtop--;
		vtop--;
	}

	return laynum[ tmp ];
}

int MaxFlow_ISAP()
{
	int flow(0);

	BFS(); // 距离求出来了

	// current优化

	memcpy(curpos,G,sizeof(G));
	
	stkmin[0] = INFi;
	mtop = 1;
	stkdx[0] = -1;
	dtop = 1;
	stkver[0] = -1;
	vtop = 1;

	top = 0; //记录内存地址
	memset(father,-1,sizeof(father));
	int st = source;
	while( dis[source]<N )
	{
		//
		int ds=-1,dsmx,mdx;
		mdx =curpos[st];
		while(mdx!=-1)
		{
			int v = mem[mdx].e;
			if( mem[mdx].residual>0 && dis[st]==dis[v]+1 )
			{
				ds = v;
				dsmx = mdx;
				break;
			}

			mdx = mem[mdx].next;
		}
		
		if(ds!=-1) //如果找到了
		{
			curpos[st] = dsmx;
			stk[top++] = dsmx;
			
			if(mem[dsmx].residual<stkmin[mtop-1])
			{
				stkmin[mtop] = mem[dsmx].residual;
				stkdx[dtop] = mtop;   // 记录栈的编号,以方便回来
				stkver[vtop] = st;    // 而其才是真正的记录该边的起点的
				mtop++;
				dtop++;
				vtop++;
			}
			else
			{
				stkmin[mtop] = stkmin[mtop-1]; 
				mtop++;
				stkdx[dtop] = stkdx[dtop-1]; 
				dtop++;
				stkver[vtop] = stkver[vtop-1]; 
				vtop++;
			}

			father[ds] = st;
			st = ds;

			if(st==sink)
			{
				// 此处要优化,当到达汇点的时候,我们要返回至最小边处

				flow += Augment();
				
				st = stkver[vtop-1];
				top = stkdx[dtop-1]-1;
				mtop = stkdx[dtop-1];
				vtop = stkdx[dtop-1];
				dtop = stkdx[dtop-1];
			}
		}
		else //如果没有找到
		{
			curpos[st] = G[st];
			if( Retreat(st) == 0 )
				break;
		}
	}

	return flow;
}

int main()
{
	read;
	write;
	int a,b,c;

	while(scanf("%d %d %d %d",&N,&NP,&NC,&M)!=-1)
	{
		source = N+1;
		sink = N+2;
		dx = 0;
		rdx = 0;

		//fill(RG,RG+N+3,-1);
		//fill(G,G+N+3,-1);
		memset(RG,-1,sizeof(RG));
		memset(G,-1,sizeof(G));

		getchar();
		
		while(M--)
		{
			scanf(" (%d,%d)%d",&a,&b,&c);
			Add_edge(++a,++b,c);
		}

		while(NP--)
		{
			scanf(" (%d)%d",&b,&c);
			Add_edge(N+1,++b,c);
		}

		while(NC--)
		{
			scanf(" (%d)%d",&a,&c);
			Add_edge(++a,N+2,c);
		}
		N+=2;

		printf("%d\n",MaxFlow_ISAP());
		
	}
	return 0;
}
posted @ 2011-03-16 15:37  AC2012  阅读(221)  评论(0编辑  收藏  举报