题目链接
很经典的一道问题,应该是二刷了,但是刷第二遍的时候还是有很深刻的体会。
题意:有N个人,每个人都有自己所可选的阵营,战士或者是法师,现在给出M个关系对,如果关系对中的两个人都是战士会得到贡献a,如果一战士一法师会得到贡献b,如果两个法师会得到贡献c,现在求最大贡献。
好了,我们来思考这个问题,有N个人,他们每个人该选择战士还是法师呢?肯定是由他的贡献来确定的,如果现在有X和Y两个人,若他两都选择了战士,那么他们将舍弃贡献是b+c,如果他两都选择了法师,则他们舍弃贡献a+b,如果一个是战士一个是法师那么舍弃的贡献呢就是a+c。
那么,我们假设我们一开始有的贡献,现在呢,我们希望舍去最少的贡献,好了,这个问题似乎变成了最小割!最小需要割去的贡献!如何建立这个最小割模型呢?
很容易想到的割法就是一边是选择战士,一边是选择法师,于是一个点,选择战士的去连接上S源点,选择法师的去连接上T汇点,这样对一个点的贡献就好求了,但是没有哪个贡献是只给一个点的,所以必须考虑到两个点的情况下。
类似于这样,选择战士所要拿走的贡献为A+B=b+c,而选择法师所要取走的贡献为C+D=a+b,可是光这样的话,选择一个战士一个法师的情况该如何解决呢?所以我们还需要解决一个很实际的问题,就是如果我们拿走的是一个战士一个法师的情况下,也就是说明一边选战士一边选法师,诶!那么是不是X和Y之间连接条边就可以了,而且这条边还比较的特殊,只有让他变成一个无向边才可以满足我们的需求,因为它要么使得一边成为战士一边成为法师,所以说它不确定是那一边战士哪一边法师,所以无向边才能解决我们现在面临的问题。
我用红色标明了我新添加的这条边。
现在,我们形成了方程组了:
小写表示的是原来的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;
}