【TOJ 3872.】Matrix【最小割】

題意:給出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));
    }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章