建图+ford最大流
建图: 如果把猪圈看作节点建图,就需要考虑顾客买猪的顺序,如果以顾客为节点则不需要。 从第一个顾客开始,从源点向顾客i连一条边,容量为没被访问过且顾客能开的猪圈猪数量的总和,如果有猪圈被顾客j访问过,那么 从j向i连一条边(有向), 容量为INF,最后让i向汇点连一条边,容量为顾客要买猪的数量。下面用的是ford算法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int Maxn = 100+10;
const int INF = 0x3f3f3f3f;
struct Edge {
int v, cap, flow, next;
} edge[Maxn*Maxn];
int h[Maxn], edge_cnt, num[Maxn*10], a[Maxn*10], tmp[Maxn*10];
bool vis[Maxn];
void add (int u, int v, int c) {
edge[edge_cnt].v = v;
edge[edge_cnt].cap = c;
edge[edge_cnt].flow = 0;
edge[edge_cnt].next = h[u];
h[u] = edge_cnt++;
edge[edge_cnt].v = u;
edge[edge_cnt].cap = 0;
edge[edge_cnt].flow = 0;
edge[edge_cnt].next = h[v];
h[v] = edge_cnt++;
}
int dfs(int cur, int t, int f) {
if(cur == t) return f;
for(int i = h[cur]; i != -1; i = edge[i].next) {
Edge &e = edge[i];
if(!vis[e.v] && e.cap > e.flow) {
vis[e.v] = true;
int flow = dfs(e.v, t, min(f, e.cap-e.flow));
if(flow > 0) {
e.flow += flow;
edge[i^1].flow -= flow;
return flow;
}
}
}
return 0;
}
void ford(int s, int t) {
int ans = 0;
while(1) {
memset(vis, false, sizeof(vis));
vis[s] = true;
int flow = dfs(s, t, INF);
if(flow == 0) {
printf("%d\n", ans); return;
}
ans += flow;
}
}
int main(void)
{
int M, N;
while(scanf("%d%d", &M, &N) != EOF) {
memset(h, -1, sizeof(h));
memset(num, 0, sizeof(num));
edge_cnt = 0;
for(int i = 1; i <= M; ++i) scanf("%d", &a[i]);
int m, v, c, sum, p;
for(int i = 1; i <= N; ++i) {
scanf("%d", &m);
sum = 0; p = 0;
while(m--) {
scanf("%d", &v);
if(num[v]) tmp[p++] = num[v];
else {
sum += a[v];
num[v] = i;
}
}
sort(tmp, tmp+p);
p = unique(tmp, tmp+p)-tmp;
for(int j = 0; j < p; ++j) add(tmp[j], i, INF);
scanf("%d", &c);
if(sum != 0) add(0, i, sum);
add(i, N+1, c);
}
ford(0, N+1);
}
return 0;
}
浙公网安备 33010602011771号