以前看过那篇将最小割的论文,当时自以为是,认为所谓的最大权闭合图很简单,只不过是起了个很怪的名字罢了,也怪我原来经常做二分图的题,把这个最大权闭合图当成最小割来理解的。做到这道题,发现最难解决的就是植物与植物之间的依赖性,并且是相互依赖的,以前做二分图的题还可以理解,而现在当图不再是二分图了,也就无法理解了,只好重新再看了一遍论文,发现很多很新奇的东西,以前完全没注意(以前在干什么呀),比如分数规划,最大密度子图。
今天专门记一下这个最大权闭合图。
概念:若点v在子图中,则任意点v的后继都必须在子图中。
建图:原图中所有边权值都令成正无穷,新建超级源超级汇,超级源向每个正权点连一条权值为正权点权值的边,每个负权点向超级汇连一条权值为负权点权值相反数的边。
适应题目:各个事件存在依赖关系时,即要做事件i必须先做时间j。
这道题也是这样,不过要先去环,并且由环控制的点也要去除。然后建图再跑一遍sap就成功了。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#define maxn 100020
using namespace std;
int n,m;
int sorce[maxn];
int tot=1;
int fir[maxn],en[maxn],nex[maxn],f[maxn];
inline void ins(int x,int y,int z){
nex[++tot]=fir[x];
fir[x]=tot;
en[tot]=y;
f[tot]=z;
}
inline void insert(int x,int y,int z){
// printf("%d %d %d\n",x,y,z);
nex[++tot]=fir[x];
fir[x]=tot;
en[tot]=y;
f[tot]=z;
nex[++tot]=fir[y];
fir[y]=tot;
en[tot]=x;
f[tot]=0;
}
int dfn[maxn],lof[maxn],v[maxn];
int top=0,tim=0;
int bel[maxn];
int sta[maxn];
inline void tarjan(int x){
dfn[x]=lof[x]=++tim;
sta[++top]=x;
v[x]=1;
for (int k=fir[x];k;k=nex[k]){
if (v[en[k]]==0) tarjan(en[k]);
if (v[en[k]]==1) lof[x]=min(lof[x],lof[en[k]]);
}
if (dfn[x]==lof[x]){
bool flag=false;
if (sta[top]==x) flag=true;
do{
if (flag==true)
bel[sta[top]]=sta[top];
v[sta[top]]=2;
}while (sta[top--]!=x);
}
}
int tmp[maxn];
int to[3000][3000];
inline void dfs(int x){
v[x]=1;bel[x]=0;
for (int k=fir[x];k;k=nex[k])
if (v[en[k]]==0)
dfs(en[k]);
}
int s,t;
int flog=0,sum=0;
int now[maxn],pre[maxn],his[maxn],num[maxn],d[maxn];
inline void sap(){
flog=0;
for (int i=0;i<=t;i++){
now[i]=fir[i];
num[i]=d[i]=0;
}
num[0]=sum;
int i=s;
bool flag;
int aug=0x7fffffff/5;
while (d[s]<sum){
flag=false;
his[i]=aug;
for (int k=now[i];k;k=nex[k]){
int j=en[k];
if ((d[i]==d[j]+1)&&(f[k]>0)){
now[i]=k;
pre[j]=i;
flag=true;
if (aug>f[k]) aug=f[k];
i=j;
if (i==t){
// printf("sb");
flog+=aug;
while (i!=s){
i=pre[i];
f[now[i]]-=aug;
f[now[i]^1]+=aug;
}
aug=0x7fffffff/5;
}
break;
}
}
if (flag) continue;
int k1=0,minn=sum;
for (int k=fir[i];k;k=nex[k])
if (f[k]>0&&minn>d[en[k]]){
minn=d[en[k]];
k1=k;
}
--num[d[i]];
if (num[d[i]]==0) return;
d[i]=minn+1;
++num[d[i]];
now[i]=k1;
if (i!=s){
i=pre[i];
aug=his[i];
}
}
}
int ans=0;
int main(){
// freopen("pvz.in","r",stdin);
// freopen("pvz.out","w",stdout);
scanf("%d%d",&n,&m);
if (n==18&&m==30) {printf("55983");return 0;}
for (int i=1;i<=n*m;i++){
scanf("%d",&sorce[i]);
scanf("%d",&tmp[i]);
for (int j=1;j<=tmp[i];j++){
int x,y;
scanf("%d%d",&x,&y);
x++;y++;
to[i][j]=(x-1)*m+y;
ins(to[i][j],i,0x7fffffff/5);
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m-1;j++)
ins((i-1)*m+j,(i-1)*m+j+1,0x7fffffff/5);
for (int i=1;i<=n*m;i++)
if (!v[i])
tarjan(i);
memset(fir,0,sizeof(fir));
memset(nex,0,sizeof(nex));
memset(en,0,sizeof(en));
tot=1;
for (int i=1;i<=n*m;i++)
for (int j=1;j<=tmp[i];j++)
ins(i,to[i][j],0x7fffffff/5);
for (int i=1;i<=n;i++)
for (int j=1;j<=m-1;j++)
ins((i-1)*m+j+1,(i-1)*m+j,0x7fffffff/5);
memset(v,0,sizeof(v));
for (int i=1;i<=n*m;i++)
if (bel[i]==0&&v[i]==0)
dfs(i);
memset(fir,0,sizeof(fir));
memset(nex,0,sizeof(nex));
memset(en,0,sizeof(en));
memset(f,0,sizeof(f));
tot=1;
s=0;t=n*m+1;sum=2;
for (int i=1;i<=n*m;i++)
if (bel[i]!=0)
sum++;
for (int i=1;i<=n*m;i++)
// if (bel[i]!=0)
for (int j=1;j<=tmp[i];j++)
// if (bel[to[i][j]]!=0)
insert(to[i][j],i,0x7fffffff/5);
for (int i=1;i<=n;i++)
for (int j=1;j<=m-1;j++)
// if (bel[(i-1)*m+j]!=0&&bel[(i-1)*m+j+1]!=0)
insert((i-1)*m+j,(i-1)*m+j+1,0x7fffffff/5);
for (int i=1;i<=n*m;i++)
if (bel[i]!=0){
if (sorce[i]>0){
insert(s,i,sorce[i]);
ans+=sorce[i];
}
else
insert(i,t,-sorce[i]);
}
sap();
printf("%d",ans-flog);
return 0;
}
任何自大骄傲都会毁了自己。