题目:
题解:
如果是知道【最大权闭合子图】的话,就可以发现这是一个明显的【最大权闭合子图】
什么是最大权闭合子图:
最大权闭合子图指要选择u,则必须要选所有与u有关系的节点。增设一个超级源点和一个超级汇点,(1->n)的点中,当点权为正时,从源点向该点连一条权值为点权大小的边,当点权为负时,从该点连一条权值大小为它的绝对值的边连向汇点。这种问题一般都是对于(u,v),如果选择u必须选择v,对(u,v)连一条容量为INF的边。
侵删,非博主原创。
最小割所产生的两个集合中,其源点S所在集合(除去S)为最大权闭合图。
最大利益=所有点正权值之和-最小割
好神奇啊
还有一个问题就是环,对于环我们是无解的,即环内所有元素都不能选,那我们干脆别把它建入图好了,也就是先tarjan去环然后再跑最大流
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#define INF 1e9
using namespace std;
const int N=605;
const int M=720000;
int tot,w[N],point[N],cur[N],deep[N],dfn[N],low[N],stack[N],remind[M],nxt[M],v[M],nn,top,tmp[N];
bool vis[N],h[N];int tot1,nxt1[M],point1[N],v1[M];
void addline(int x,int y,int z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remind[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remind[tot]=0;
}
void addline1(int x,int y){++tot1; nxt1[tot1]=point1[x]; point1[x]=tot1; v1[tot1]=y;}
bool bfs(int s,int t)
{
queue<int>q;memset(deep,0x7f,sizeof(deep));
for (int i=s;i<=t;i++) cur[i]=point[i];
deep[s]=0;q.push(s);
while (!q.empty())
{
int now=q.front();q.pop();
for (int i=point[now];i!=-1;i=nxt[i])
if (remind[i] && deep[v[i]]>INF) deep[v[i]]=deep[now]+1,q.push(v[i]);
}
return deep[t]<INF;
}
int dfs(int now,int t,int limit)
{
if (now==t || !limit) return limit;
int flow=0,f;
for (int i=cur[now];i!=-1;i=nxt[i])
{
cur[now]=i;
if (deep[v[i]]==deep[now]+1 && (f=dfs(v[i],t,min(limit,remind[i]))))
{
flow+=f;
limit-=f;
remind[i]-=f;
remind[i^1]+=f;
if (!limit) return flow;
}
}
return flow;
}
int dinic(int s,int t)
{
int ans=0;
while (bfs(s,t)) ans+=dfs(s,t,INF);
return ans;
}
void tarjan(int x)
{
dfn[x]=low[x]=++nn; vis[x]=1; stack[++top]=x;
for (int i=point1[x];i;i=nxt1[i])
if (!dfn[v1[i]])
{
tarjan(v1[i]);
low[x]=min(low[x],low[v1[i]]);
}else if (vis[v1[i]]) low[x]=min(low[x],dfn[v1[i]]);
if (low[x]==dfn[x])
{
int now=0,lj=0;
while (now!=x)
{
now=stack[top--]; tmp[++lj]=now;
vis[now]=0;
}
if (lj!=1) for (int i=1;i<=lj;i++) h[tmp[i]]=1;
}
}
int main()
{
tot=-1;memset(point,-1,sizeof(point));
int n,m,sum=0;scanf("%d%d",&n,&m);
for (int i=1;i<=n*m;i++)
{
int k,x,y;
scanf("%d%d",&w[i],&k);
for (int j=1;j<=k;j++)
{
scanf("%d%d",&x,&y);x++;y++;int zb=(x-1)*m+y;
addline(zb,i,INF); addline1(zb,i);
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=j+1;k<=m;k++)
addline((i-1)*m+j,(i-1)*m+k,INF),addline1((i-1)*m+j,(i-1)*m+k);
for (int i=1;i<=n*m;i++)
if (!dfn[i]) tarjan(i);
for (int i=1;i<=n*m;i++)
if (!h[i])
{
if (w[i]>0) addline(0,i,w[i]),sum+=w[i];
else addline(i,n*m+1,-w[i]);
}
printf("%d",sum-dinic(0,n*m+1));
}