ZOJ 2601 Warehouse Keeper(费用流)

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;
}

版权声明:本文为博主原创文章,未经博主允许不可转载。

时间: 2024-10-11 07:09:34

ZOJ 2601 Warehouse Keeper(费用流)的相关文章

zoj 3231(上下界费用流)

题意:树上每个节点上有若干苹果,边上带权,问你最小费用使得书上的苹果方差最小. 思路:上下费用流问题,参考http://blog.csdn.net/qq564690377/article/details/8870587 代码如下: 1 /************************************************** 2 * Author : xiaohao Z 3 * Blog : http://www.cnblogs.com/shu-xiaohao/ 4 * Last m

【网络流24题】No.19 负载平衡问题 (费用流)

[题意] G 公司有 n 个沿铁路运输线环形排列的仓库, 每个仓库存储的货物数量不等. 如何用最少搬运量可以使 n 个仓库的库存数量相同.搬运货物时,只能在相邻的仓库之间搬运. 输入文件示例input.txt517 9 14 16 4 输出文件示例output.txt11 [分析] 其实我觉得这题可以贪心啊..n^2贪心??.没细想.. 打的是费用流.. 大概这样建图: 懒得写了..凌乱之美.. 求满流费用.. 1 #include<cstdio> 2 #include<cstdlib&

POJ 3422 kaka&#39;s matrix trvals(费用流)

#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <set> #include <map> #include <cma

hdu 2448 Mining Station on the Sea【网络费用流】

Mining Station on the Sea Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2371    Accepted Submission(s): 732 Problem Description The ocean is a treasure house of resources and the development

POJ训练计划3422_Kaka&#39;s Matrix Travels(网络流/费用流)

解题报告 题目传送门 题意: 从n×n的矩阵的左上角走到右下角,每次只能向右和向下走,走到一个格子上加上格子的数,可以走k次.问最大的和是多少. 思路: 建图:每个格子掰成两个点,分别叫"出点","入点", 入点到出点间连一个容量1,费用为格子数的边,以及一个容量∞,费用0的边. 同时,一个格子的"出点"向它右.下的格子的"入点"连边,容量∞,费用0. 源点向(0,0)的入点连一个容量K的边,(N-1,N-1)的出点向汇点连一

POJ 2135 Farm Tour &amp;&amp; HDU 2686 Matrix &amp;&amp; HDU 3376 Matrix Again 费用流求来回最短路

累了就要写题解,最近总是被虐到没脾气. 来回最短路问题貌似也可以用DP来搞,不过拿费用流还是很方便的. 可以转化成求满流为2 的最小花费.一般做法为拆点,对于 i 拆为2*i 和 2*i+1,然后连一条流量为1(花费根据题意来定) 的边来控制每个点只能通过一次. 额外添加source和sink来控制满流为2. 代码都雷同,以HDU3376为例. #include <algorithm> #include <iostream> #include <cstring> #in

【BZOJ3502/2288】PA2012 Tanie linie/【POJ Challenge】生日礼物 堆+链表(模拟费用流)

[BZOJ3502]PA2012 Tanie linie Description n个数字,求不相交的总和最大的最多k个连续子序列. 1<= k<= N<= 1000000. Sample Input 5 2 7 -3 4 -9 5 Sample Output 13 题解:跟1150和2151差不多. 我们先做一些预处理,因为连续的正数和连续的负数一定是要么都选要么都不选,所以可以将它们合并成一个数,同时区间中的零以及左右两端的负数没有意义,可以将它们删掉.然后我们得到的序列就变成:正-

POJ 2195 Going Home(费用流)

http://poj.org/problem?id=2195 题意: 在一个网格地图上,有n个小人和n栋房子.在每个时间单位内,每个小人可以往水平方向或垂直方向上移动一步,走到相邻的方格中.对每个小人,每走一步需要支付1美元,直到他走入到一栋房子里.每栋房子只能容纳一个小人. 计算出让n个小人移动到n个不同的房子需要支付的最小费用. 思路: 源点和每个人相连,容量为1,费用为0. 汇点和每栋房子相连,容量为1,费用为0. 每个人和每栋房子相连,容量为1,费用为人和房子之间的距离. 这样一来,跑一

洛谷P3381——费用流模板题

嗯..随便刷了一道费用流的模板题....来练练手. #include<iostream> #include<cstdio> #include<cstring> using namespace std; int h[5210],d[5210],used[5210],que[100010],last[5210]; int k=1,INF=0x7fffffff,ans1=0,ans2=0; inline int read(){ int t=1,num=0; char c=ge