[BZOJ-1280]猪 题解
题目
题目描述
Emmy在一个养猪场工作。这个养猪场有M个锁着的猪圈,但Emmy并没有钥匙。顾客会到养猪场来买猪,一个接着一个。每一位顾客都会有一些猪圈的钥匙,他们会将这些猪圈打开并买走固定数目的猪。所有顾客有的钥匙和他们需要买猪的数量在事先都告诉了Emmy,于是Emmy要订一个计划,使得卖出去的猪最多。
买卖的过程是这样的:一个顾客前来,并打开所有他可以打开的猪圈。然后Emmy从这些猪圈里牵出固定数目的猪卖给顾客(最多只能和顾客需要数相等),并可以重新安排这些开着的猪圈中的猪。
每个猪圈可以存放任意数目的猪。 写一个程序,使得Emmy能够卖出去尽可能多的猪。输入
输入第一行有两个整数:\(M\) 和 \(N\),表示猪圈数和顾客数。
第二行有 \(M\) 个整数,表示每个猪圈初始时有多少猪。
接下来的 \(N\) 行按照前来的次序描述了每一个顾客,每行的格式如下:\(A\ K_1\ K_2…K_A\ B\)
\(A\) 表示该顾客拥有的钥匙数,\(K_1...K_A\) 表示每个钥匙所对应的猪圈,这些猪圈是不重复的,\(B\) 表示该顾客需要购买的猪的数目。3 3 3 1 10 2 1 2 2 2 1 3 3 1 2 6
输出
输出仅包含一个整数,即最多能卖出去的猪的数目。
7
数据范围
\(1 ≤ M ≤ 1000,1 ≤ N ≤ 100\) , 每个猪圈猪的头数不超过 \(1000\) ,每个人打算买的头数不超过 \(10000\) 。
思路分析
刚学网络流,老师留的思考题。
难点就在建图。
初始思路
刚开始想到可以从源点到每个猪圈连一条容量为猪圈初始猪数量的边。
-
每来一个人,就将其可以开的猪圈的最近点(后面会解释)到这个人的点连边,容量正无穷。
-
每个人的点到汇点连一条容量为其购买数的边。
-
每个人的点再到 \(k\) 个新点连边,容量正无穷,再将这个人可以开的猪圈一一对应到这 \(k\) 个点,更新猪圈最近点。
like this:
但是这样会发现复杂度过不去......
优化 1
其实仔细观察就会发现,上述的第 3 步其实完全没有必要每个人再分出一堆点,只需要更新这些猪圈的最近点为这个人即可,效果一样。
like this:
到目前为止,已经可以过去这道题了,这也是机房大佬想出来的解法。
优化 2
但是老师似乎提到了另一种优化:
没必要将猪圈建点,可以之间从源点向原来需要从猪圈连边的点连边,容量为需要连边的猪圈猪数之和。
like this:
现在这图不就很简单了,建完图一遍最大流就解决了。
Code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=1500,INF=0x3f3f3f3f;
int h[M],nxt[N],w[N],ver[N],co=1;
int a[M],pre[N];
int n,m,s,t;
int tmp[M];
void add(int x,int y,int z){
nxt[++co]=h[x],h[x]=co,ver[co]=y,w[co]=z;
nxt[++co]=h[y],h[y]=co,ver[co]=x,w[co]=0;
}
int d[M],no[M];
bool bfs(){
memset(d,0,sizeof d);
queue<int> q;
d[s]=1,no[s]=h[s];q.push(s);
while(q.size()){
int x=q.front();q.pop();
for(int i=h[x];i;i=nxt[i]){
int y=ver[i];
if(!d[y] && w[i]){
d[y]=d[x]+1,no[y]=h[y];
q.push(y);
if(y==t) return 1;
}
}
}
return 0;
}
int dfs(int x,int fl){
if(x==t) return fl;
int res=fl,i,k;
for(i=no[x];i && res;i=nxt[i]){
int y=ver[i];
if(d[y]==d[x]+1 && w[i]){
k=dfs(y,min(res,w[i]));
if(!k) d[y]=0;
res-=k;
w[i]-=k;
w[i^1]+=k;
}
}
no[x]=i;
return fl-res;
}
int main(){
cin>>n>>m;
s=1,t=2;
for(int i=1;i<=n;++i) cin>>a[i],pre[i+2]=i+2;
for(int i=1;i<=m;++i){
int k,x,y;cin>>k;
for(int j=1;j<=k;++j){
cin>>x;y=pre[x+2];
if(y==x+2) tmp[1]+=a[x];
else tmp[y]=INF;
pre[x+2]=i+n+2;
}
for(int j=1;j<n+i+2;++j){
if(tmp[j]) add(j,i+n+2,tmp[j]),tmp[j]=0;
}
cin>>k;
add(i+n+2,t,k);
}
int flow=0,ans=0;
while(bfs()){
while(flow=dfs(1,INF)) ans+=flow;
}
cout<<ans;
return 0;
}