最大权闭合图
关于最大权闭合图的那套理论,去看胡伯涛论文《最小割模型在信息学竞赛中的应用》吧,orz学长。
顺便做点笔记:
最大权闭合图解决这样一类问题:如果选了一个点, 则这个点所有可达的点都必须选,求最大点权和。
最大权闭合图的建图流程:
1.增加源s汇t
2.源s连接原图的正权点,容量为相应点权
3.原图的负权点连接汇t,容量为相应点权的相反数
4.原图边的容量为正无限.
最终答案就是:正点权和 - 最大流
最终选出的点集就是在残量网络中从S可达的所有点,也就是最小割后S的连通块里除了S的所有点。
套路?按胡学长PPT里的话说就是:在任意带权有向图中,只要有依赖关系需要解决,最大权闭合图都普遍适用。(普适性)
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define N 55005
using namespace std;
namespace runzhe2000
{
const int S = N-1, T = N-2, INF = 1<<29;
int ecnt = 1, n, m, last[N], cur[N], sum, level[N];
struct edge{int next, to, flow;}e[N<<3];
void _addedge(int a, int b, int c){e[++ecnt]=(edge){last[a],b,c}; last[a]=ecnt;}
void addedge(int a, int b, int c){_addedge(a,b,c);_addedge(b,a,0);}
bool bfs()
{
queue<int> q; q.push(S); memset(level,0,sizeof(level)); level[S] = 1;
for(;!q.empty();)
{
int x = q.front(); q.pop();
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(level[y] || !e[i].flow) continue;
level[y] = level[x] + 1; if(y == T) return 1; q.push(y);
}
} return 0;
}
int dfs(int x, int flow)
{
if(x == T) return flow; int use = 0;
for(int &i = cur[x]; i; i = e[i].next)
{
int y = e[i].to; if(level[x] + 1 != level[y]) continue;
int w = dfs(y, min(flow-use,e[i].flow));
e[i].flow -= w; e[i^1].flow += w; use += w;
if(use == flow) return use;
} return use;
}
int dinic(){int ret = 0; for(; bfs(); ) memcpy(cur, last, sizeof(last)), ret += dfs(S,INF); return ret;}
void main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
{
int p; scanf("%d",&p);
addedge(i, T, p);
}
for(int i = 1; i <= m; i++)
{
int a, b, c; scanf("%d%d%d",&a,&b,&c);
sum += c; addedge(S, i+n, c);
addedge(i+n, a, INF);
addedge(i+n, b, INF);
}
printf("%d\n",sum - dinic());
}
}
int main()
{
runzhe2000::main();
}