【poj1149】pigs

描述 Description

尼克在一家养猪场工作,这家养猪场共有M间锁起来的猪舍,由于猪舍的钥匙都给了客户,所以尼克没有办法打开这些猪舍,客户们从早上开始一个接一个来购买生猪,他们到达后首先用手中的钥匙打开他所能打开的全部猪舍,然后从中选取他要买的生猪,尼克可以在此期间将打开的猪舍中的猪调整到其它开着的猪舍中,每个猪舍能存放的猪的数量是没有任何限制的。买完猪后客户会将他打开的猪舍关上。
好在尼克事先知道每位客户手中有哪些钥匙,要买多少猪,以及客户到来的先后次序。请你写一个程序,帮助尼克求出最多能卖出多少头生猪。

输入格式 Input Format

输入文件的第一行包含两个整数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为该客户要买的生猪的头数。

输出格式 Output Format

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

样例输入 Sample Input

3 3
3 1 10
2 1 2 2
2 1 3 3
1 2 6

样例输出 Sample Output

7

题解 Solution

众所周知网络流难在建图。这道题我刚开始以为是源点连人【权值为需求数】,人连房间【权值为需求数】,房间汇点【是猪的个数】。然而建完图连手推样例都不行,GG。

顺手翻了翻hzwer.com,然后就找到了,发现一个神犇的建图方法:

  1. 每个顾客向汇点连接他的需求数

  2. 对于每个猪圈,源点向第一个打开他的顾客连一条权值为猪数量的边

  3. 每个顾客向下一个顾客连接一个权值为正无穷的边

对于样例,我们可以建图:

以顾客为点,如图所示

Sample

【其中的4是猪圈1+2的和,因为他开了两个】最大流为7,符合样例

为什么这样建图?看题中所给的条件:每个顾客打开门后,尼克可以把猪迁移到开的房间。那么显然一个顾客对后面的顾客有影响。那么进入每个顾客的流量显然是这个顾客第一个打开的所有猪舍的和,这样向下一个顾客流,就相当于把猪的位置调换了。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include<bits/stdc++.h>
#define ll long long
#define INF 2100000000
using std::min;
using std::cin;
using std::cout;
using std::endl;

int n,m;
int q[2100];
int ans=0;
int len=0;
int s,t;
int level[2100];
int rev[50010];
int vis[2100];
int lin[2100];
int a[2100];
int ha=0;

struct qaq
{
int nt,y,v;
}e[100010];

char buf[1<<15],*fs,*ft;
inline char getc(){ return (fs==ft && (ft= (fs=buf) + fread(buf,1,1<<15,stdin) , fs==ft) )?0:*fs++; }
int read()
{
char ch=getc();int k=0,f=1;
while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getc(); }
while(isdigit(ch)) { k=(k<<3)+(k<<1)+ch-'0'; ch=getc(); }
return k*f;
}

void insert(int x,int y,int v)
{
e[++len].nt=lin[x]; lin[x]=len; e[len].v=v; e[len].y=y; rev[len]=len+1;
e[++len].nt=lin[y]; lin[y]=len; e[len].v=0; e[len].y=x; rev[len]=len-1;
}

bool make_level()
{
int head=0,tail=1;
q[1]=s;
memset(level,-1,sizeof(level));
level[s]=0;
while(head<tail)
{
int x=q[++head];
for(int i=lin[x];i;i=e[i].nt)
{
if(e[i].v && level[e[i].y]==-1)
{
level[e[i].y]=level[x]+1;
q[++tail]=e[i].y;
}
}
}
return level[t]>=0;
}

int max_flow(int k,int flow)
{
if(k==t) return flow;
int maxflow=0;
int v;
for(int i=lin[k];i && maxflow<flow;i=e[i].nt)
if(e[i].v && level[e[i].y]==level[k]+1)
if(v=max_flow(e[i].y,min(e[i].v,flow-maxflow)))
maxflow+=v , e[i].v-=v , e[rev[i]].v+=v;
if(!maxflow) level[k]=-1;
return maxflow;
}

void dinic()
{
int v;
while(make_level())
while(v=max_flow(s,INF))
ans+=v;
}

int main()
{
//freopen("a.txt","r",stdin);
m=read();n=read();//m is pig
//cout<<n<<' '<<m<<endl;
memset(vis,0,sizeof(vis));
s=0;
t=n+1;
for(int i=1;i<=m;i++) a[i]=read();
for(int i=1;i<=n;++i)
{
int key=read();
for(int j=1;j<=key;++j)
{
int val=read();
if(!vis[val]) insert(s,i,a[val]);
else insert(vis[val],i,INF);
vis[val]=i;
}
key=read();
insert(i,t,key);
}

dinic();
printf("%d\n",ans);
return 0;
}