poj 1149

     又是一个网络流的构图题。不是那么容易就想得到的,尤其是没有接触到很多网络流题目时。构图时由于人的数目较少,因此就只用人做结点。对于每一个猪舍的钥匙,若是第一次出现,则源点与该人连接一条边,容量为该猪舍的初始猪的数目,否则,当前人与前一个有此钥匙的人连一条边,容量无穷大。最后每一个人和汇点连一条边,容量为是他想买的猪的数目,最后,求一次最大流即可。

 

这里有个很详细的解题报告:

 

http://imlazy.ycool.com/post.2059102.html

 

     以下是代码:

 

 

#include<cstdio>
#include<cstring>
using namespace std;
const int M=10000;
const int N=1200;
const int INF=1<<29;
struct node
{
	int u,v,cap,flow;
	int next; 
}edge[2*M];
int head[N],num;
int store[N],vis[N];
int level[N];
int m,n,s1,t1;

void init()
{
	for(int i=0;i<=n+m+3;i++)
		head[i]=-1;
	num=0;
	s1=0;t1=n+m+1;
}

void addege(int u,int v,int cap)
{
	edge[num].u=u;
	edge[num].v=v;
	edge[num].cap=cap;
	edge[num].flow=0;
	edge[num].next=head[u];
	head[u]=num++;
	edge[num].u=v;
	edge[num].v=u;
	edge[num].cap=0;
	edge[num].flow=0;
	edge[num].next=head[v];
	head[v]=num++;
}

int dinic_dfs(int sta,int end,int ver)
{
	int stack[N*10],top=0;
	int flow=0,cur,ptr,pre[N],minf,i;
	int del[N];
	for(i=0;i<=ver;i++)
	{
		del[i]=0;
	}
	stack[top++]=sta;
	pre[sta]=sta;
	cur=sta;
	while(top)
	{
		while(cur!=end && top)
		{
			for(i=head[cur];i!=-1;i=edge[i].next)
			{
				int v=edge[i].v;
				if(level[v]==level[cur]+1 && edge[i].cap>edge[i].flow && !del[v])
				{
					stack[top++]=v;
					cur=v;
					pre[v]=i;
					break;
				}
			}
			if(i==-1)
			{
				del[cur]=1;
				top--;
				if(top)
					cur=stack[top-1];
			}
		}
		if(cur==end)
		{
			minf=INF;
			while(cur!=sta)
			{
				cur=pre[cur];
				if(edge[cur].cap-edge[cur].flow<minf) minf=edge[cur].cap-edge[cur].flow;
				cur=edge[cur].u;
			}
			cur=end;
			while(cur!=sta)
			{
				cur=pre[cur];
				edge[cur].flow+=minf;
				edge[cur^1].flow-=minf;
				if(edge[cur].cap-edge[cur].flow==0)
				{
					ptr=edge[cur].u;
				}
				cur=edge[cur].u;
			}
			while(top>0 && stack[top-1]!=ptr) top--;
			if(top) cur=stack[top-1];
			flow+=minf;
		}
	}
	return flow;
}

bool dinic_bfs(int sta,int end,int ver)
{
	int i,j;
	int queue[10*N];
	for(i=0;i<=ver;i++)
	{
		level[i]=-1;
	}
	int rear=0;
	queue[rear++]=sta;
	level[sta]=0;
	for(i=0;i<rear;i++)
	{
		for(j=head[queue[i]];j!=-1;j=edge[j].next)
		{
			int v=edge[j].v;
			if(level[v]==-1 && edge[j].cap>edge[j].flow)
			{
				level[v]=level[queue[i]]+1;
				queue[rear++]=v;
			}
		}
	}
	return level[end]>=0;
}

int dinic(int sta,int end,int ver)
{
	int flow=0,t;
	while(dinic_bfs(sta,end,ver))
	{
		t=dinic_dfs(sta,end,ver);
		if(t) flow+=t;
		else break;
	}
	return flow;
}

int main()
{
 // 	freopen("in.txt","r",stdin);
	scanf("%d%d",&m,&n);
	init();
	int i,j;
	int a,b,c;
	memset(vis,0,sizeof(vis));
	for(i=1;i<=m;i++)
		scanf("%d",&store[i]);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a);
		for(j=0;j<a;j++)
		{
			scanf("%d",&b);
			if(!vis[b])
			{
				addege(s1,i,store[b]);
			}
			else
				addege(vis[b],i,INF);
			vis[b]=i;
		}
		scanf("%d",&c);
		addege(i,t1,c);
	}
	printf("%d/n",dinic(s1,t1,t1+1));
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值