[2014BOP预选赛_C]格格取数

来源:互联网 发布:网络电视台解决方案 编辑:程序博客网 时间:2024/04/28 15:48

解题思路:

觉得是最小费用流,但是当时不知道怎么建图。

首先很容易想到这样的做法:源s向行i连一条流量为1,费用为0的边,列j同样向汇t连一条流量为1,费用为0的边,然后ij间连一条流量为1,费用为a[i][j]的边。如果n==m,显然这样做保证了每行每列都被取到了。

但是当n!=m的时候,总有一些行和列没有匹配到。

就算是n==m的时候,我们只是保证了每行每列都被取到,但是这却不一定是最优解。

比如:

0 0 0

1 1 0

1 1 0

归根结底是我们限制了每行每列最多只能取一次。

解决方法的方法是加入补充边集。

过于抽象我也不知道怎么说。

参考:http://blog.csdn.net/cqsh3vj2/article/details/23624495?reload

首先约束覆盖,保证每行每列都取到了。

然后提供边,让每行每列允许取多次。

至于s-t中连一条容量为m*n,费用为0的边的原因是:这个图显然最大流是m*n,若不加这条边那么就等于整个矩阵的数取了个遍(先满足最大流再满足最小费用),也就是这条边是用来分流的。

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#include <map>#include <set>#include <vector>#include <utility>#include <queue>#include <stack>#include <cstdlib>#include <ctime>using namespace std;#pragma comment(linker,"/STACK:102400000,102400000")#define LL long long#define ULL unsigned long long#define Hei cout << "Czy!!!" << endl;#define lson rt << 1, l, mid#define rson  rt << 1 | 1, mid + 1, r#define MOD 1000000007#define INF 10000#define maxn 100/*    clock_t t1, t2;    t1 = clock();    t2 = clock();    cout << (double)(t2 - t1) / CLOCKS_PER_SEC << endl;*/int a[maxn][maxn];struct Edge{    int from, to, cap, flow, cost;    Edge(int from = 0, int to = 0, int cap = 0, int flow = 0, int cost = 0): from(from), to(to), cap(cap), flow(flow), cost(cost) {}};vector<Edge> edges;vector<int> G[maxn];void AddEdge(int from, int to, int cap, int cost){    edges.push_back(Edge(from, to, cap, 0, cost));    edges.push_back(Edge(to, from, 0, 0, -cost));    int m = edges.size();    G[from].push_back(m - 2);    G[to].push_back(m - 1);}struct MCMF{    int n, s, t;    int inq[maxn];    int d[maxn];    int p[maxn];    int a[maxn];    void init(int n)    {        this->n = n;        for (int i = 0; i <= n; i++) G[i].clear();        edges.clear();    }    bool Spfa(int s, int t, int &flow, int &cost)    {        for (int i = 0; i <= n; i++) d[i] = INF;        memset(inq, 0, sizeof(inq));        d[s] = 0;        inq[s] = 1;        p[s] = 0;        a[s] = INF;        queue<int> Q;        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.cost)                {                    d[e.to] = d[u] + e.cost;                    p[e.to] = G[u][i];                    a[e.to] = min(a[u], e.cap - e.flow);                    if (!inq[e.to])                    {                        Q.push(e.to);                        inq[e.to] = 1;                    }                }            }        }        if (d[t] == INF) return false;        flow += a[t];        cost += d[t] * a[t];        int u = t;        while (u != s)        {            edges[p[u]].flow += a[t];            edges[p[u] ^ 1].flow -= a[t];            u = edges[p[u]].from;        }        return true;    }    int Mincost(int s, int t)    {        int flow = 0, cost = 0;        while (Spfa(s, t, flow, cost));        return cost;    }}solver;int main(){    int T;    cin >> T;    while (T--)    {        int n, m;        cin >> n >> m;        solver.init(2 * n + 2 * m + 3);        for (int i = 1; i <= n; i++)            for (int j = 1; j <= m; j++) scanf("%d", &a[i][j]);        for (int i = 1; i <= n; i++)            for (int j = 1; j <= m; j++)            {                AddEdge(i, 2 * n + j, 1, a[i][j]);                AddEdge(i, 2 * n + m + j, 1, a[i][j]);                AddEdge(n + i, 2 * n + j, 1, a[i][j]);                AddEdge(n + i, 2 * n + m + j, 1, a[i][j]);            }        for (int i = 1; i <= n; i++)        {            AddEdge(0, i, 1, -INF);            AddEdge(0, i + n, m, 0);        }        for (int i = 1; i <= m; i++)        {            AddEdge(2 * n + i, 2 * n + 2 * m + 1, 1, -INF);            AddEdge(2 * n + m + i, 2 * n + 2 * m + 1, m, 0);        }        AddEdge(0, 2 * n + 2 * m + 1, m * n, 0);        AddEdge(2 * n + 2 * m + 2, 0, m * n, 0);        AddEdge(2 * n + 2 * m + 1, 2 * n + 2 *m + 3, m * n, 0);        //for (int i = 1; i <= m; i++) AddEdge(i + n, 2 * n + m + 1, 1, -INF);        int x = solver.Mincost(2 * n + 2 * m + 2, 2 * n + 2 * m + 3) ;        cout << x  + (n + m) * INF<< endl;    }}


0 0