luogu p4174 最大获利(最大权闭合子图)
luogu p4174 最大获利(最大权闭合子图)
给定n个点,m条边,每条边有一个贡献,每个点有一个代价。选择一条边,会付出边所连两个点的代价,问最大代价。
我们换个建图方式:把图G中的边\(e_i\)也建成G‘中的点\(E_i\)。由于在G中,选择了一条边\(e_i\)必须选择边旁的点\(V_{i_1}\)和\(V_{i2}\),所以我们在G‘中,将\(E_i\)连两条向\(V_{i1}\)和\(V_{i2}\)的有向边。对G’求最大权子图即可。
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e5+5, maxm=2e5+5, INF=1e9;
int n, m, src, dst, ans;
inline int min(int x, int y){ return x<y?x:y; }
struct Edge{
int to, nxt, f;
}e[maxm*2];
int fir[maxn], cnte=1;
void addedge(int x, int y, int v){
Edge &ed=e[++cnte];
ed.to=y; ed.nxt=fir[x]; ed.f=v; fir[x]=cnte;
}
int q[maxn], head, tail, dep[maxn];
bool bfs(){
memset(dep, 0, sizeof(dep)); dep[src]=1;
head=tail=0; q[tail++]=src; int u;
while (head<tail){
u=q[head++];
for (int i=fir[u]; ~i; i=e[i].nxt)
if (e[i].f&&!dep[e[i].to]){
dep[e[i].to]=dep[u]+1;
q[tail++]=e[i].to;
}
}
return dep[dst];
}
int cur[maxn];
int dfs(int u, int flow){
if (u==dst) return flow;
for (int i=cur[u]; ~i; i=e[i].nxt, cur[u]=i)
if (dep[e[i].to]==dep[u]+1&&e[i].f){
int minm=dfs(e[i].to, min(flow, e[i].f));
e[i].f-=minm; e[i^1].f+=minm;
if (minm) return minm;
}
return 0;
}
int Dinic(){
int ans=0, t;
while (bfs()){
memcpy(cur, fir, sizeof(fir));
while (t=dfs(src, INF)) ans+=t;
}
return ans;
}
int main(){
memset(fir, -1, sizeof(fir));
scanf("%d%d", &n, &m); int t, ta, tb;
src=0; dst=n+m+1;
for (int i=1; i<=n; ++i){
scanf("%d", &t);
addedge(src, i, t); addedge(i, src, 0);
}
for (int i=1; i<=m; ++i){
scanf("%d%d%d", &ta, &tb, &t); ans+=t;
addedge(ta, i+n, INF); addedge(i+n, ta, 0);
addedge(tb, i+n, INF); addedge(i+n, tb, 0);
addedge(i+n, dst, t); addedge(dst, i+n, 0);
}
ans-=Dinic();
printf("%d\n", ans);
return 0;
}