POJ-1149 PIGS

题目大意:

有M个猪圈,每个猪圈都有把锁,卖猪的本身是没有钥匙的,现在有N个顾客要来买猪,而且第i个顾客有a[i]把锁的钥匙,能打开k1,k2,k3...kai的猪圈,称这个时候,你可以调整k1,k2,k3...kai号猪圈里面猪的个数。现在已知每个客户要买多少猪,有哪些锁。问你一天最多能卖出去多少猪。

解题思路:

把顾客当作除了源点和汇点的节点,并且设置一个源点和汇点。

把第一次访问ai个猪圈的顾客与源点添加一条边,容量为开始时猪圈中猪的数目。把在他之后第二个访问该猪圈的顾客与他再添加一条边,容量为INF。以此类推。

再把每个顾客与汇点添加一条边容量为要买猪的个数。

这样构图就完成了。然后直接求网络流即可

代码:

#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;

typedef struct node{
    int v, cap, nxt;
    node(int _v = 0, int _cap = 0, int _nxt = 0): v(_v), cap(_cap), nxt(_nxt){}
}Edge;

const int maxn = 200;
const int maxm = 2e4 + 10;
const int INF = 0x3f3f3f3f;

int tot;
Edge edge[maxm];
int dis[maxn];
int head[maxm], num[1005], vis[maxn];

inline int Min(int a, int b){
    return a < b ? a : b;
}
void add(int u, int v, int cap){
    edge[tot] = Edge(v, cap, head[u]);
    head[u] = tot++;
    edge[tot] = Edge(u, 0, head[v]);
    head[v] = tot++;
}
int bfs(int s, int t){
    queue<int> q;
    while(!q.empty()) q.pop();
    memset(dis, 0, sizeof(dis));
    q.push(s); dis[s] = 1;
    while(!q.empty()){
        int u = q.front(); q.pop();
        if(u == t) break;
        for(int i = head[u]; ~i; i = edge[i].nxt){
            Edge &e = edge[i];
            if(!dis[e.v] && e.cap){
                dis[e.v] = dis[u] + 1;
                q.push(e.v);
            }
        }
    }
    return dis[t];
}
int dfs(int u, int v, int f){
    if(u == v) return f;
    int sum = 0;
    for(int i = head[u]; ~i; i = edge[i].nxt){
        Edge &e = edge[i];
        if(e.cap && dis[e.v] == dis[u] + 1){
            int ret = dfs(e.v, v, Min(f, e.cap));
            sum += ret; f -= ret;
            e.cap -= ret; edge[i^1].cap += ret;
        }
    }
    return sum;
}
int dinic(int s, int t){
    int ret = 0;
    while(bfs(s, t))
        ret += dfs(s, t, INF);
    return ret;
}
int main(){
    int a, k, m, n, sum, val;
    while(~scanf("%d%d", &m, &n)){
        tot = 0;
        memset(vis, 0, sizeof(vis));
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= m; ++i){
            scanf("%d", &num[i]);
        }
        for(int i = 1; i <= n; ++i){
            scanf("%d", &a);
            sum = 0;
            for(int j = 1; j <= a; ++j){
                scanf("%d", &k);
                if(!vis[k]){ vis[k] = i; sum += num[k]; }
                else {
                    if(vis[k] == i) continue;
                    add(vis[k], i, INF);
                    vis[k] = i;
                }
            }
            scanf("%d", &val);
            if(sum > 0) add(0, i, sum);
            add(i, n + 1, val);
        }
        printf("%d
", dinic(0, n + 1));
    }
    return 0;
}