PIGS

描述

尼克在一家养猪场工作,这家养猪场共有M间锁起来的猪舍,由于猪舍的钥匙都给了客户,所以尼克没有办法打开这些猪舍,客户们从早上开始一个接一个来购买生猪,他们到达后首先用手中的钥匙打开他所能打开的全部猪舍,然后从中选取他要买的生猪,尼克可以在此期间将打开的猪舍中的猪调整到其它开着的猪舍中,每个猪舍能存放的猪的数量是没有任何限制的。买完猪后客户会将他打开的猪舍关上。

好在尼克事先知道每位客户手中有哪些钥匙,要买多少猪,以及客户到来的先后次序。请你写一个程序,帮助尼克求出最多能卖出多少头生猪。

 

输入

输入文件的第一行包含两个整数M和N,1≤M≤1000,1≤N≤100,M为猪舍的数量,N为客户人数,猪舍的编号为1到M,客户的编号为1到N。

输入文件第二行包含M个空格隔开的整数,依次表示每个猪舍中的生猪数量,每个整数大于等于0,且小于等于1000。

接下来的N行每行表示一位客户的购买信息,第I个客户的购买信息位于第I+2行,其格式如下:

A  K1 K2……KA  B

它表示该客户共有A把钥匙,钥匙编号依次为KK2……KA,且K1<K2<……<KA,B为该客户要买的生猪的头数。

 

输出

    输出文件仅有一行包含一个整数,表示尼克最多能卖出的生猪的头数。

 

样例

PIGS.IN

3 3

3 1 10

2 1 2 2

2 1 3 3

1 2 6

 

PIGS.OUT

7


现在真是越来越弱了,连个最大流都看了这么久T_T……十分感动……

好吧,建了一个SB的图,骗了30'后,我终于知道sap的去环优化是多么重要了……

然后无耻地看了题解(其实我的所有边跟题解都是反过来的,╮(╯▽╰)╭,果真是“与众不同”的思维):

把猪圈和购买者都看成图中的点。加一个源,源往每个猪圈连一条有向边,权值即为该猪圈里原有猪的数目。加一个汇,每个购买者往汇连一条有向边,权值为他想买的猪的数目。 然后就是购买者与猪圈之间的边了。依次考虑每一个人。就样例来说,第一个人要第1、2号猪圈里的猪,那么就把1、2号猪圈各向他连一条有向边。第二个人要1、3号猪圈里的猪,而1号猪圈已经被第一个人动过了,那就从第一个人往第二个人引一条有向边;3号猪圈没有被之前的人动过,于是把3号猪圈往第二个人引一条有向边。类推,接下来是第一个人往第三个人引一条有向边。猪圈与人之间,以及人与人之间的边,权值都是无穷大。 

这种状态去省选是会死人的啊!

好吧,rp++

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#define MaxN 2000
using namespace std;
const int INF=~0U>>3;
int head[MaxN],ht[MaxN],v[MaxN],h[MaxN],tot;
int u[MaxN];
int m,n,k,x,y,s,t,num;
struct edge
{
	int v,cap,next;
	edge(int x,int y,int c):v(y),cap(c),next(head[x])
	{
		head[x]=tot++;
	}
};
vector<edge> a;
inline void AddEdge(int x,int y,int c)
{
	a.push_back(edge(x,y,c));
	a.push_back(edge(y,x,0));
}
inline void init()
{
	cin>>m>>n;
	t=n+m+1;
	num=t+1;
	memset(head,-1,sizeof(head));
	for(int i=1;i<=m;i++)
		scanf("%d",&x),
		AddEdge(i+n,t,x);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&k);
		for(int j=0;j<k;j++)
		{
			scanf("%d",&x);
			if(!u[x])
				AddEdge(i,x+n,INF),u[x]=i;
			else
				AddEdge(i,u[x],INF);
		}
		scanf("%d",&x);
		AddEdge(s,i,x);
	}
	memcpy(ht,head,sizeof(head));
	v[0]=num;
}
int sap(int x,int lim)
{
	if(x==t) return lim;
	int y,sum=0,flow;
	for(int i=head[x];~i;i=a[i].next)
	{
		y=a[i].v;
		if(a[i].cap&&h[y]+1==h[x])
			head[x]=i,
			flow=sap(y,min(lim-sum,a[i].cap)),
			a[i].cap-=flow,
			a[i^1].cap+=flow,
			sum+=flow;
		if(sum==lim) return sum;
	}
	if(h[s]>=num) return sum;
	head[x]=ht[x];
	if(!--v[h[x]]) h[s]=num;
	v[++h[x]]++;
	return sum;
}
inline void work()
{
	int ans=0;
	while(h[s]<num)
		ans+=sap(s,INF);
	cout<<ans<<endl;
}
int main()
{
	init();
	work();
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值