用时:debug时间90分钟。
先读m再读n
我是傻逼(1/1)
题目大意:
有(m)个房子,(n)个顾客。第(i)个房子里有(a_i)只猪;第(i)个顾客可以买至多(b_i)只猪,且可以打开房子(c_1,c_2,...c_j),这些同时被打开的房子中,剩余的猪的可以互相转移。求最多卖出的猪数量。
很难想到是网络流,知道是网络流也很难建图。
网络流建模汇总 - Edelweiss里有对这道题很详细的解释。
- 首先设置超级源点(S)和超级汇点(T);
设当前顾客为(i),打开的一个房子为(x),起始时猪的数量为(a_x)
- 顾客(i)向(T)连边,权值为(i)的购买上限;
- 如果(x)第一次被打开,(S)向(i)连边,权值为(a_x);
- 如果(x)不是第一次被打开,上一个打开(x)的顾客(last[x])向(i)连边,权值为(INF)。
即,(last[x])购买后剩余的猪的数量都可以转移给(i)。
(code)
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#define MogeKo qwq
using namespace std;
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
int n,m,k,s,t,cnt,ans;
int head[maxn],to[maxn],nxt[maxn],w[maxn];
int cur[maxn],dis[maxn],last[maxn],a[maxn];
void add(int x,int y,int z) {
to[++cnt] = y;
nxt[cnt] = head[x];
head[x] = cnt;
w[cnt] = z;
}
void addedge(int x,int y,int z) {
add(x,y,z);
add(y,x,0);
}
bool bfs(){
memset(dis,0,sizeof(dis));
queue <int> q;
dis[s] = 1;
q.push(s);
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if(!dis[v] && w[i]) {
dis[v] = dis[u]+1;
q.push(v);
}
}
}
return dis[t];
}
int dfs(int u,int flow) {
if(u == t) return flow;
int sum = 0;
for(int &i = cur[u]; i; i = nxt[i]) {
int v = to[i];
if(dis[v] == dis[u]+1 && w[i]) {
int ff = dfs(v,min(flow,w[i]));
flow -= ff;
sum += ff;
w[i] -= ff;
w[i^1] += ff;
if(!flow) break;
}
}
if(!sum) dis[u] = -1;
return sum;
}
void dinic() {
while(bfs()) {
for(int i = 0; i <= n+1; i++)
cur[i] = head[i];
ans += dfs(s,INF);
}
}
int main() {
scanf("%d%d",&m,&n);
for(int i = 1; i <= m; i++)
scanf("%d",&a[i]);
s = 0,t = n+1,cnt = 1;
for(int i = 1; i <= n; i++) {
int x,y = 0;
scanf("%d",&k);
while(k--) {
scanf("%d",&x);
if(!last[x]) y += a[x];
else addedge(last[x],i,INF);
last[x] = i;
}
addedge(s,i,y);
scanf("%d",&y);
addedge(i,t,y);
}
dinic();
printf("%d",ans);
return 0;
}