版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37632935/article/details/84654969
题意:给你一个n点m边的图,每个点有个权值,每条边有个权值,让你选择一个边集,然后最大化(边集的权值减去边集中包含的点集的权值)。
思路:BZOJ3894:文理分科类似的题目,建立一个汇点T,每个点i向T连一条容量为a[i]的边,每条边拆成一个点,向这条边所连的两个点连一条容量为INF的边,建立源点S,向每个边拆成的点连一条容量为w的边。答案为sum(w)-最小割。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const ll N = 1e5 + 5;
ll n, m;
ll sp, tp;
struct node{
ll v, next;
ll cap;
}edge[N * 2];
ll head[N], deg[N], cur[N];
ll cnt = 0;
void inti()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void add(ll u, ll v, ll cap)
{
edge[cnt].v = v;
edge[cnt].cap = cap;
edge[cnt].next = head[u];
head[u] = cnt++;
edge[cnt].v = u;
edge[cnt].cap = 0;
edge[cnt].next = head[v];
head[v] = cnt++;
}
ll bfs()
{
memset(deg, -1, sizeof(deg));
deg[sp] = 0;
queue<ll> q;
q.push(sp);
while(!q.empty())
{
ll u = q.front();
if(u == tp) return 1;
q.pop();
for(ll i = head[u]; ~i; i = edge[i].next)
{
ll v = edge[i].v;
ll cap = edge[i].cap;
if(deg[v] == -1 && cap)
{
deg[v] = deg[u] + 1;
q.push(v);
}
}
}
return 0;
}
ll dfs(ll u, ll flow)
{
if(u == tp || flow == 0) return flow;
ll res = 0, f;
for(ll &i = cur[u]; ~i; i = edge[i].next)
{
ll v = edge[i].v;
ll cap = edge[i].cap;
if(deg[v] == deg[u] + 1 && (f = dfs(v, min(flow - res,cap))) > 0)
{
edge[i].cap -= f;
edge[i ^ 1].cap += f;
res += f;
if(res == flow) return flow;
}
}
if(!res) deg[u] = -1;
return res;
}
ll dinic()
{
ll ans = 0;
while(bfs())
{
memcpy(cur, head, sizeof(head));
ans += dfs(sp, INF);
}
return ans;
}
int main()
{
cin >> n >> m;
sp = 0, tp = n+m+1;
inti();ll sum=0;
for(ll i=1;i<=n;i++)
{
ll x;cin>>x;
add(i,tp,x);
}
for(ll i = 1,j=i+n; i <= m; i++,j++)
{
ll u,v,x;
scanf("%lld %lld %lld",&u,&v,&x);
add(sp,j,x);sum+=x;
add(j,u,10000000000000000);
add(j,v,10000000000000000);
}
cout << sum-dinic() << endl;
return 0;
}