【TOJ 3872.】Matrix【最小割】

3 篇文章 0 订阅
3 篇文章 0 订阅

题意:给出n×n的矩阵B,1×n的矩阵C,求出一个只含0 1的1×n的矩阵A,使得D=( A×B-C ) × AT最大,输出D。

思路:

         首先我们可以推出公式 

           

         继续整理:

          

        从上式可以看出只要使得括号里的值最小,D就得到最大值。我们可以转化为最小割问题,因为ai只能取1,0。我们将ai=1的划分到S集合中,ai = 0划分到T集合中,我们先对所有的ai都与源点S连边,与汇点T也连边。

        这样如果我们割掉的是S与ai的边,就说明ai = 0,则贡献的值是∑bij,如果割掉的是T与ai的边,就说明ai = 1,则贡献的值为ci,现在我们来看中间的那一项,如果割了ai与aj的边就说明ai = 1, aj = 0, 贡献的值是bij。贡献的值分别是边的流量,这样构造出来的最小割就是我们满足题意的括号中的值,最后用∑∑bij-最小割就是答案,这也是我们经典的项目分配问题。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1005
#define M 2040007
#define inf 0x7f7f7f7f

struct E
{
       int v, ne, f;
       E(){}
       E(int _v, int _ne, int _f):v(_v),ne(_ne),f(_f){}
}e[M];

int head[N], size, out[N], lev[N], q[N];

void init()
{
     memset(head, -1, sizeof(head));
     size = 0;
}

void add(int u, int v, int f)
{
     e[size] = E(v, head[u], f);
     head[u] = size++;
     e[size] = E(u, head[v], 0);
     head[v] = size++;
}

int bfs(int S,int T)
{
    int u, rear = 0, v;
    memset(lev, -1, sizeof(lev));
    lev[S] = 0, q[rear ++] = S;
    for(int i = 0; i < rear; i ++) {
        u = q[i];
        for(int j = head[u]; j != -1; j = e[j].ne) {
            v = e[j].v;
            if(e[j].f && lev[v] == -1)
            {
                lev[v] = lev[u] + 1, q[rear ++] = v;
                if(v == T) return 1;
            }
        }
    }
    return 0;
}

int dfs(int cur, int a,  int T)
{
    if(cur == T)
        return a;
    int v, f;
    for(int &i = out[cur]; i != -1; i = e[i].ne) {
        v = e[i].v, f = e[i].f;
        if(f && lev[v] == lev[cur] + 1)
        {
            int t = dfs(v, min(a, f), T);
            if(t)
            {
                e[i].f -= t, e[i ^ 1].f += t;
                return t;
            }
        }
    }
    return 0;
}

int dinic(int S,int T)
{
    int ans = 0;
    while(bfs(S,T))
    {
        memcpy(out, head, sizeof(head));
        while(int t = dfs(S, inf, T))
            ans += t;
    }
    return ans;
}

int b[N][N], c[N], sb[N];

int main() {
    int T, n, i, j, sum;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        sum = 0;
        for (i = 1;i <= n;i++) {
            for (j = 1;j <= n;j++) {
                scanf("%d", &b[i][j]);
                sum += b[i][j];
            }
        }
        for (i = 1;i <= n;i++) {
            sb[i] = 0;
            for (j = 1;j <= n;j++) sb[i] += b[j][i];
        }
        for (i = 1;i <= n;i++) scanf("%d", &c[i]);
        init();
        for (i = 1;i <= n;i++) {
            add(0, i, sb[i]), add(i, n+1, c[i]);
            for (j = 1;j <= n;j++) {
                if (i == j) continue;
                add(i, j, b[j][i]);
            }
        }
        printf("%d\n", sum-dinic(0, n+1));
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值