描述
尼克在一家养猪场工作,这家养猪场共有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把钥匙,钥匙编号依次为K1 K2……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;
}