zoj 3229 Shoot the Bullet--有源汇 有上下界 最大流 递归和非递归sap

/*
	题目大意:某人n天给m个美女拍照
	每个美女都有一个最低要求
	每天有一个照片数量上限
	先给出n m
	每个美女的下限
	n个{ 第i天拍照的美女数  这天的照片数量上限
		依次是  美女编号  这天的下限  上限
	}

	有源汇 有上下界 最大流

	先对 附加 源汇ss tt求依次最大流
	若有可行流 再对s t求最大流

	这是第二次写这个题,第一次写的挺不顺利
	(可能是构图的时候出问题了,不是超时就是错,求最大流的方法就换了三种:sap递归,sap非递归,dinic
	我都怀疑我的最大流模版是不是有漏洞,下面的递归sap就是最初用的,当时我还以为这个有问题,
	其实,还真有一个问题,下面有,但是该了还是超时,今天看来,是构图的问题)

	下面的  非递归sap跑了330ms   递归sap跑了270ms  看来我的模版速度还是不错的
*/
#include<stdio.h>
#include<string.h>
#define inf 0x7fffffff
struct edge//边
{
	int u,v,f,next,b,c;//边的 前节点 后节点 可用流 下条边的编号  原来边上流的上下界
}e[400000];
int head[1400],in[1400],out[1400],n,m,s,t,ss,tt,yong,indexx[400000],ind,sum;
/*
head 链表头  in  某点的 流入下界和 - 流出下界和  out没用到
yong 指向下一个未用的边  indexx 存储某个美女某天的边的编号index在string.h里边有所以改成这个了  ind 表示下一个未用的 indexx
sum  流入tt的流量和  判断有无可行解用
*/
void ini()//初始化
{
	memset(head,-1,sizeof(head));
	yong=0;
	memset(in,0,sizeof(in));

	s=m+n,t=s+1,ss=t+1,tt=ss+1;
	ind=0;
	sum=0;
}
void adde(int from,int to,int xia,int shang)//加边
{//加边
	e[yong].u=from,e[yong].v=to,e[yong].f=shang-xia,e[yong].b=xia,e[yong].c=shang;
	e[yong].next=head[from],head[from]=yong++;
//同时加它的退边
	e[yong].u=to,e[yong].v=from,e[yong].f=0,e[yong].b=xia,e[yong].c=shang;
	e[yong].next=head[to],head[to]=yong++;
}
void build()//构造必要弧
//http://blog.csdn.net/qq172108805/article/details/7783010上有关于这个的简要说明
{
	int i;
	for(i=0;i<=t;++i)
	{
		if(in[i]>0)
			adde(ss,i,0,in[i]);
		else
		{
			adde(i,tt,0,-in[i]);
			sum+=(-in[i]);
		}
	}
}
int sap(int s,int t)//非递归sap
{
	int i,j,ff=0,v,cur[1400],d[1400],num[1400],low[1400],pre[1400];
	memset(low,0,sizeof(low));
	memset(d,0,sizeof(d));
	memset(num,0,sizeof(num));
	for(i=0;i<=t;++i)
		cur[i]=head[i];
	low[i=s]=inf;
	num[s]=t+1;
	while(d[s]<t+1)
	{
		for(j=cur[i];j!=-1;j=e[j].next)
			if(e[j].f>0&&d[i]==d[v=e[j].v]+1)
				break;
		cur[i]=j;
		if(j>=0)
		{
			pre[v]=j;
			low[v]=e[j].f;
			if(low[v]>low[i])
				low[v]=low[i];
			i=v;
			if(i==t)
			{
				for(;i!=s;i=e[pre[i]].u)
				{
					e[pre[i]].f-=low[t];
					e[pre[i]^1].f+=low[t];
				}
				ff+=low[t];
				memset(low,0,sizeof(low));
				low[s]=inf;
			}
		}else
		{
			if(--num[d[i]]==0)
				break;
			d[i]=t+1;
			cur[i]=head[i];
			for(j=head[i];j!=-1;j=e[j].next)
				if(e[j].f>0&&d[i]>d[e[j].v]+1)
					d[i]=d[e[j].v]+1;
			num[d[i]]++;
			if(i!=s)
				i=e[pre[i]].u;
		}
	}
	return ff;
}
int d[1400],num[1400];
int min(int a,int b){return a<b?a:b;}
int sap_gap(int u,int f,int s,int t)//递归sap
{
	if(u==t)
		return f;
	int i,v,mind=t,last=f,cost;
	for(i=head[u];i!=-1;i=e[i].next)
	{
		v=e[i].v;
		int flow=e[i].f;
		if(flow>0)//参考模版写的时候把flow写成了f
		{
			if(d[u]==d[v]+1)
			{
				cost=sap_gap(v,min(last,flow),s,t);
				e[i].f-=cost;
				e[i^1].f+=cost;
				last-=cost;

				if(d[s]>=t+1)
					return f-last;

				if(last==0)
					break;
			}
			if(d[v]<mind)
				mind=d[v];
		}
	}

	if(last==f)
	{
		--num[d[u]];
		if(num[d[u]]==0)
			d[s]=t+1;
		d[u]=mind+1;
		++num[d[u]];
	}
	return f-last;
}
int max_f(int s,int t)//调用递归sap
{
	int f=0;
	memset(d,0,sizeof(d));
	memset(num,0,sizeof(num));
	for(num[s]=t+1;d[s]<t+1;)
		f+=sap_gap(s,inf,s,t);
	return f;
}
int main()
{
	int i,a,c,b,ng,most,sun;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		ini();
		for(i=0;i<m;i++)
		{
			scanf("%d",&a);
			adde(i,t,a,inf);
			in[t]+=a;
			in[i]-=a;
		}
		for(;i<m+n;++i)
		{
			scanf("%d%d",&ng,&most);
			adde(s,i,0,most);
			while(ng--)
			{
				scanf("%d%d%d",&a,&b,&c);
				indexx[ind++]=yong;
				adde(i,a,b,c);
				in[a]+=b;
				in[i]-=b;
			}
		}
		
		adde(t,s,0,inf);

		build();

		//sun=sap(s,tt);//非递归sap
		sun=max_f(ss,tt);//递归sap
		if(sun!=sum)
		{
			printf("-1\n");
		}else
		{
			//sap(s,t);//非递归sap
			max_f(s,t);//递归sap
			b=0;
			for(i=0;i<ind;++i)
			{
				a=indexx[i];
				b+=e[a].c-e[a].f;
			}
			printf("%d\n",b);
			for(i=0;i<ind;++i)
			{
				a=indexx[i];
				printf("%d\n",e[a].c-e[a].f);
			}
		}
		printf("\n");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值