Harmonious Army 【HDU - 6598】【最小割】

題目鏈接


  很經典的一道問題,應該是二刷了,但是刷第二遍的時候還是有很深刻的體會。

  題意:有N個人,每個人都有自己所可選的陣營,戰士或者是法師,現在給出M個關係對,如果關係對中的兩個人都是戰士會得到貢獻a,如果一戰士一法師會得到貢獻b,如果兩個法師會得到貢獻c,現在求最大貢獻。

  好了,我們來思考這個問題,有N個人,他們每個人該選擇戰士還是法師呢?肯定是由他的貢獻來確定的,如果現在有X和Y兩個人,若他兩都選擇了戰士,那麼他們將捨棄貢獻是b+c,如果他兩都選擇了法師,則他們捨棄貢獻a+b,如果一個是戰士一個是法師那麼捨棄的貢獻呢就是a+c。

  那麼,我們假設我們一開始有\sum(a_i + b_i + c_i)的貢獻,現在呢,我們希望捨去最少的貢獻,好了,這個問題似乎變成了最小割!最小需要割去的貢獻!如何建立這個最小割模型呢?

  很容易想到的割法就是一邊是選擇戰士,一邊是選擇法師,於是一個點,選擇戰士的去連接上S源點,選擇法師的去連接上T匯點,這樣對一個點的貢獻就好求了,但是沒有哪個貢獻是隻給一個點的,所以必須考慮到兩個點的情況下。

  類似於這樣,選擇戰士所要拿走的貢獻爲A+B=b+c,而選擇法師所要取走的貢獻爲C+D=a+b,可是光這樣的話,選擇一個戰士一個法師的情況該如何解決呢?所以我們還需要解決一個很實際的問題,就是如果我們拿走的是一個戰士一個法師的情況下,也就是說明一邊選戰士一邊選法師,誒!那麼是不是X和Y之間連接條邊就可以了,而且這條邊還比較的特殊,只有讓他變成一個無向邊纔可以滿足我們的需求,因爲它要麼使得一邊成爲戰士一邊成爲法師,所以說它不確定是那一邊戰士哪一邊法師,所以無向邊才能解決我們現在面臨的問題。

  我用紅色標明瞭我新添加的這條邊。

  現在,我們形成了方程組了:

A + B = b + c

C + D = a + b

A + E + D = B + E + C = a + c

  小寫表示的是原來的a、b、c,大寫是我們給邊對應的權值。

  我們可以知道的是A == B,並且呢,C == D。然後我們就可以確定完整的A、B、C、D、E各自的值了。

  於是,根據A、B、C、D、E的值,我們建圖跑最大流,用總和減去自然就是我們要的答案了。

  此題中,可能存在".5"這樣的小數,所以呢,我給所有的邊權都“ * 2 ”來避免出現浮點數了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f3f3f3f3f
#define eps 1e-8
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
#define MAX_3(a, b, c) max(a, max(b, c))
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 5e2 + 7, maxM = 1e5 + 7;
int N, M, head[maxN], cnt, cur[maxN];
struct Eddge
{
    int nex, to; ll flow;
    Eddge(int a=-1, int b=0, ll c=0):nex(a), to(b), flow(c) {}
}edge[maxM];
inline void addEddge(int u, int v, ll w)
{
    edge[cnt] = Eddge(head[u], v, w);
    head[u] = cnt++;
}
inline void _add(int u, int v, ll w) { addEddge(u, v, w); addEddge(v, u, 0); }
struct Max_Flow
{
    int gap[maxN], d[maxN], que[maxN], ql, qr, S, T, node;
    inline void init()
    {
        for(int i=0; i<=node + 1; i++)
        {
            gap[i] = d[i] = 0;
            cur[i] = head[i];
        }
        ++gap[d[T] = 1];
        que[ql = qr = 1] = T;
        while(ql <= qr)
        {
            int x = que[ql ++];
            for(int i=head[x], v; ~i; i=edge[i].nex)
            {
                v = edge[i].to;
                if(!d[v]) { ++gap[d[v] = d[x] + 1]; que[++qr] = v; }
            }
        }
    }
    inline ll aug(int x, ll FLOW)
    {
        if(x == T) return FLOW;
        ll flow = 0;
        for(int &i=cur[x], v; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(d[x] == d[v] + 1)
            {
                ll tmp = aug(v, min(FLOW, edge[i].flow));
                flow += tmp; FLOW -= tmp; edge[i].flow -= tmp; edge[i ^ 1].flow += tmp;
                if(!FLOW) return flow;
            }
        }
        if(!(--gap[d[x]])) d[S] = node + 1;
        ++gap[++d[x]]; cur[x] = head[x];
        return flow;
    }
    inline ll max_flow()
    {
        init();
        ll ret = aug(S, INF);
        while(d[S] <= node) ret += aug(S, INF);
        return ret;
    }
} mf;
inline void init()
{
    cnt = 0; mf.S = 0; mf.T = N + 1; mf.node = mf.T + 1;
    for(int i=0; i<=mf.node; i++) head[i] = -1;
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        init();
        ll sum = 0, A, B, C, D, E;
        for(int i=1, u, v, a, b, c; i<=M; i++)
        {
            scanf("%d%d%d%d%d", &u, &v, &a, &b, &c);
            sum += a + b + c;
            A = B = a / 4 + 4 * c / 3;
            C = D = 5 * a / 4 + c / 3;
            E = a / 2 + c / 3;
            _add(mf.S, u, A);
            _add(mf.S, v, B);
            _add(u, mf.T, C);
            _add(v, mf.T, D);
            addEddge(u, v, E);
            addEddge(v, u, E);
        }
        printf("%lld\n", sum - mf.max_flow() / 2LL);
    }
    return 0;
}

 

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