uva 10779 Collectors Problem(最大流)

uva 10779 Collectors Problem

Some candy manufacturers put stickers into candy bar packages. Bob and his friends are collecting
these stickers. They all want as many different stickers as possible, but when they buy a candy bar,
they don’t know which sticker is inside.
It happens that one person has duplicates of a certain sticker. Everyone trades duplicates for stickers
he doesn’t possess. Since all stickers have the same value, the exchange ratio is always 1:1.
But Bob is clever: he has realized that in some cases it is good for him to trade one of his duplicate
stickers for a sticker he already possesses.
Now assume, Bob’s friends will only exchange stickers with Bob, and they will give away only
duplicate stickers in exchange with different stickers they don’t possess.
Can you help Bob and tell him the maximum number of different stickers he can get by trading
stickers with his friends?
Input
The first line of input contains the number of cases T (T ≤ 20). The first line of each case contains
two integers n and m (2 ≤ n ≤ 10, 5 ≤ m ≤ 25). n is the number of people involved (including Bob),
and m is the number of different stickers available.
The next n lines describe each person’s stickers; the first of these lines describes Bob’s stickers.
The i-th of these lines starts with a number ki ≤ 50 indicating how many stickers person i has.
Then follows ki numbers between 1 and m indicating which stickers person i possesses.
Output
For each case, print the test case number together with the maximum number of different stickers Bob
can get.
Explanation of the sample output:
In the first case, no exchange is possible, therefore Bob can have only the sticker with number 1.
In the second case, Bob can exchange a sticker with number 1 against a sticker with number 2 of
the second person, and then this sticker against a sticker with number 3 or 4 of the third person, and
now he has stickers 1, 2 and 3 or 1, 2 and 4.
Sample Input
2
2 5
6 1 1 1 1 1 1
3 1 2 2
3 5
4 1 2 1 1
3 2 2 2
5 1 3 4 4 3
Sample Output
Case #1: 1
Case #2: 3

题目大意:Bob和他的朋友从糖果包装里收集贴纸。这些朋友每人手里都有一些(有重复)贴纸,并且只跟别人交换他所没有的贴纸。贴纸总是一对一交换。Bob比这些朋友更聪明,因为他意识到只跟别人交换自己没有的贴纸并不总是最优的。在某些情况下,换来一张重复的更划算。假设Bob的朋友之歌Bob交换(朋友之间不进行交换),并且这些朋友只会让出手里重复的贴纸来交换他们没有的不同贴纸。你的任务是帮助Bob算出,他最终能获得的最大数量的贴纸。
解题思路:设置一个超级源点,连向所有的贴纸,容量为Bob手里拥有该种贴纸的数量。设置一个超级汇点,使所有的贴纸连向它,容量为1。还要设置除Bob以外其他人的点,作为中转。如果第i个人的第j种贴纸大于1张,则建一条从i + m到j边,容量为这个人拥有的第j种贴纸数量减1,这个人得留一张。如果第i个人没有第j种贴纸,则建一条从j到i + m的边,容量为1,这个人只需要一张。建完图,跑最大流,结果就是答案。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;

typedef long long ll;
const int N = 100;
const int M = 500;
const int INF = 0x3f3f3f3f;
int n, m, s, t;
int rec[35];

struct Dinic{
    int ec, head[N], first[N], que[N], lev[N];
    int Next[M], to[M], v[M];

    void init() {
        ec = 0;
        memset(first, -1, sizeof(first));
    }

    void addEdge(int a,int b,int c) {
        to[ec] = b;
        v[ec] = c;
        Next[ec] = first[a];
        first[a] = ec++;

        to[ec] = a;
        v[ec] = 0;
        Next[ec] = first[b];
        first[b] = ec++;
    }

    int BFS() {
        int kid, now, f = 0, r = 1, i;
        memset(lev, 0, sizeof(lev));
        que[0] = s, lev[s] = 1;
        while (f < r) {
            now = que[f++];
            for (i = first[now]; i != -1; i = Next[i]) {
                kid = to[i];    
                if (!lev[kid] && v[i]) {
                    lev[kid] = lev[now] + 1;    
                    if (kid == t) return 1;
                    que[r++] = kid;
                }
            }
        }
        return 0;
    }

    int DFS(int now, int sum) {
        int kid, flow, rt = 0;
        if (now == t || sum == 0) return sum;
        for (int i = head[now]; i != -1 && rt < sum; i = Next[i]) {
            head[now] = i;  
            kid = to[i];
            if (lev[kid] == lev[now] + 1 && v[i]) {
                flow = DFS(kid, min(sum - rt, v[i]));
                if (flow) {
                    v[i] -= flow;
                    v[i^1] += flow;
                    rt += flow;
                } else lev[kid] = -1;   
            }           
        }
        return rt;
    }

    int dinic() {
        int ans = 0;
        while (BFS()) {
            for (int i = 0; i < N; i++) {
                head[i] = first[i];
            }           
            ans += DFS(s, INF);
        }
        return ans;
    }   
}din;

void input() {
    scanf("%d %d", &n, &m);
    s = 0, t = n + m + 10;
    int a, b;
    memset(rec, 0, sizeof(rec));
    scanf("%d", &a);
    for (int i = 0; i < a; i++) {
        scanf("%d", &b);    
        rec[b]++;
    }
    for (int i = 1; i <= m; i++) {
        if (rec[i]) {
            din.addEdge(s, i, rec[i]);  
        }
    }
    for (int i = 1; i < n; i++) {
        scanf("%d", &a);
        memset(rec, 0, sizeof(rec));
        for (int j = 0; j < a; j++) {
            scanf("%d", &b);    
            rec[b]++;
        }
        for (int j = 1; j <= m; j++) {
            if (rec[j] > 1) {
                din.addEdge(i + m, j, rec[j] - 1);  
            } else if (rec[j] == 0) {
                din.addEdge(j, i + m, 1);   
            }   
        }
    }
    for (int i = 1; i <= m; i++) {
        din.addEdge(i, t, 1);
    }
}

int main() {
    int T, Case = 1;
    scanf("%d", &T);
    while (T--) {
        din.init();
        input();
        printf("Case #%d: %d\n", Case++, din.dinic());
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值