ZOJ 2601 Warehouse Keeper

The company where Jerry works owns a number of warehouses that can be used to store various goods. For each warehouse the types of goods that can be stored in this warehouse are known. To avoid problems with taxes, each warehouse must store only one type of goods, and each type of goods must be stored in at most one warehouse.

Jerry is planning to receive a new lot of goods in a couple of days and he must store the goods in the warehouses. However there are some goods in some warehouses already and Jerry wants to move as few of them as possible.

Help him to find the maximal number of types of goods that he can store in the warehouses and the minimal number of goods he must move in order to do that.

Input

The input contains multiple test cases. The first line of the input is a single integer T (1 <= T <= 40) which is the number of test cases. T test cases follow, each preceded by a single blank line.

The first line of each test case contains integer numbers m and n (2 <= m, n <= 200) - the number of warehouses and the number of types of goods respectively.

The following m lines describe warehouses. Each line contains ki - the number of various types of goods that can be stored in this warehouse (remember, only one type of goods can be stored in a warehouse at a time), followed by ki integer numbers - the types of goods that can be stored.

The last line contains m integer numbers - for each warehouse either 0 is provided if there is no goods in this warehouse, or the type of goods that is currently stored in this warehouse if there is one. It is guaranteed that the initial configuration is correct, that is, each warehouse stores the goods it can store, and no type of goods is stored in more than one warehouse.

Output

For each case, on the first line print p - the maximal number of types of goods that can be stored in the warehouses, and q - the minimal number of goods that need to be moved in order to do that. After that output m integer numbers - for each warehouse output the type of goods that must be stored in this warehouse, or 0 if none must be.

Remember that you may only move goods that are already stored in some houses to other ones, you are not allowed to dispose them.

Two consecutive cases should be separated by a single blank line. No blank line should be produced after the last test case.

Sample Input

2

4 5
3 1 2 3
2 1 2
2 1 2
3 1 4 5
0 2 0 1

2 2
1 1
1 2
0 0

Sample Output

4 1
3 2 1 4

2 0
1 2

题目大意:有n个仓库,m种货物,每个仓库只能存放指定的几种货物。每当一个仓库存放了一种货物以后,便不可以再存放其他种类的货物。现在,有一些仓库已经存放了货物,问在尽量少的移动仓库中已有的货物的前提下,怎么让这n个仓库存放尽量多种类的货物。

解题思路:设置一个超级源点,连向所有的仓库,容量为1,费用为0。设置一个超级汇点,使所有的货物种类连向超级汇点,容量为1,费用为0。然后,仓库,连向他所能存放的所有货物种类,容量都为1,但费用不同,若该种货物为该仓库中已存放的货物种类,那么费用为0,若不是费用为1,这样跑费用流的时候,就能优先使用已存在仓库中的货物,避免不必要的交换。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
using namespace std;

typedef long long ll;
const int N = 600;
const int M = 50000;
const ll INF = 1e18;
vector<int> wk[N];
int n, m, s, t;
int rec[N];
int pre[N], inq[N]; 
ll a[N], d[N];
struct Edge{
    int from, to;
    ll cap, flow;
    ll cos;
};

vector<Edge> edges;
vector<int> G[M];

void init() {
    memset(rec, 0, sizeof(rec));
    for (int i = 0; i <= n; i++) wk[i].clear(); 
    for (int i = 0; i <= n + m + n * m; i++) G[i].clear();
    edges.clear();
}

void addEdge(int from, int to, ll cap, ll cos) {
    edges.push_back((Edge){from, to, cap, 0, cos});
    edges.push_back((Edge){to, from, 0, 0, -cos});
    int m = edges.size();
    G[from].push_back(m - 2);
    G[to].push_back(m - 1);
}

int BF(int s, int t, ll& flow, ll& cost) {
    queue<int> Q;
    memset(inq, 0, sizeof(inq));
    memset(a, 0, sizeof(a));
    memset(pre, 0, sizeof(pre));
    for (int i = 0; i < N; i++) d[i] = INF;
    d[s] = 0;
    a[s] = INF;
    inq[s] = 1;
    pre[s] = 0;
    Q.push(s);
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        inq[u] = 0;
        for (int i = 0; i < G[u].size(); i++) {
            Edge &e = edges[G[u][i]];
            if (e.cap > e.flow && d[e.to] > d[u] + e.cos) {
                d[e.to] = d[u] + e.cos;
                a[e.to] = min(a[u], e.cap - e.flow);
                pre[e.to] = G[u][i];
                if (!inq[e.to]) {
                    inq[e.to] = 1;
                    Q.push(e.to);
                }
            }   
        }
    }
    if (d[t] == INF) return 0;
    flow += a[t];
    cost += (ll)d[t] * (ll)a[t];
    for (int u = t; u != s; u = edges[pre[u]].from) {
        edges[pre[u]].flow += a[t];
        edges[pre[u]^1].flow -= a[t];
    }
    return 1;
}

int MCMF(int s, int t, ll& cost) {
    ll flow = 0;
    cost = 0;       
    while (BF(s, t, flow, cost));
    return flow;
}

void input() {
    s = 0, t = n + m + 3;
    int a, b;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a);    
        for (int j = 0; j < a; j++) {
            scanf("%d", &b);        
            wk[i].push_back(b);
        }
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", &rec[i]);   
    }
}

void build() {
    for (int i = 1; i <= n; i++) addEdge(s, i, 1, 0);
    for (int i = 1; i <= m; i++) addEdge(i + n, t, 1, 0);
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < wk[i].size(); j++) {
            if (wk[i][j] == rec[i]) addEdge(i, rec[i] + n, 1, 0);   
            else addEdge(i, wk[i][j] + n, 1, 1);
        }   
    }
}

void print() {
    int ans[N];
    ll cost;
    int temp = MCMF(s, t, cost);
    printf("%d", temp);
    int flag2;
    for (int i = 1; i <= n; i++) {
        flag2 = 0;
        for (int j = 0; j < G[i].size(); j++) {
            if (edges[G[i][j]].flow > 0) {
                flag2 = 1;
                ans[i] = edges[G[i][j]].to - n;
            }
        }   
        if (!flag2) {
            ans[i] = 0;
        }
    }   
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (!rec[i]) continue;
        if (rec[i] != ans[i]) cnt++;
    }
    printf(" %d\n", cnt);
    printf("%d", ans[1]);
    for (int i = 2; i <= n; i++) {
        printf(" %d", ans[i]);
    }puts("");
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &n, &m);
        init();
        input();
        build();
        print();
        if (T) puts("");
    }
    return 0;
}