每个植物向它能保护的植物连边,矩阵中每个点向它右边相邻的点连边。
用拓扑排序去除相互保护的植物所成的环,发现将剩余代表保护的边倒置后,答案即为最大权闭合子图。
代码如下。
#include<iostream> #include<cstdio> #include<algorithm> #include<string> #include<cstring> #include<cmath> #include<queue> #include<vector> using namespace std; typedef long long ll; const int N=1e4+10,M=1e6+10,inf=0x3f3f3f3f; int n,m,s,t,ans,tot=1,a[N],d[N],v[N],deg[N],head[N],ver[M],next[M],edge[M];vector<int> g[N];queue<int> q; inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } inline void add(int x,int y,int z){ ver[++tot]=y;edge[tot]=z;next[tot]=head[x];head[x]=tot; ver[++tot]=x;edge[tot]=0;next[tot]=head[y];head[y]=tot; } inline bool bfs(){ memset(d,0,sizeof(d)); queue<int> q;d[s]=1;q.push(s); while(q.size()){ int x=q.front();q.pop(); for(int i=head[x];i;i=next[i]){ int y=ver[i],z=edge[i]; if(d[y]||!z) continue; d[y]=d[x]+1;q.push(y); if(y==t) return 1; } } return 0; } inline int dinic(int x,int flow){ if(x==t) return flow; int rest=flow; for(int i=head[x];i&&rest;i=next[i]){ int y=ver[i],z=edge[i]; if(d[y]!=d[x]+1||!z) continue; int k=dinic(y,min(z,rest)); if(!k) d[y]=0; else edge[i]-=k,edge[i^1]+=k,rest-=k; } return flow-rest; } int main(){ n=read();m=read();s=n*m;t=s+1; for(int i=0;i<s;i++){ a[i]=read();int p=read(); while(p--){ int x=read(),y=read(); g[i].push_back(x*m+y);deg[x*m+y]++; } } for(int i=0;i<n;i++){ for(int j=1;j<m;j++){ g[i*m+j].push_back(i*m+j-1);deg[i*m+j-1]++; } } for(int i=0;i<s;i++) if(!deg[i]) q.push(i),v[i]=1; while(q.size()){ int x=q.front();q.pop(); for(int i=0;i<g[x].size();i++){ int y=g[x][i]; if(!v[y]&&!--deg[y]) q.push(y),v[y]=1; } } for(int x=0;x<s;x++){ if(!v[x]) continue; for(int i=0;i<g[x].size();i++){ int y=g[x][i];if(!v[y]) continue; add(y,x,inf); } if(a[x]>0) add(s,x,a[x]),ans+=a[x]; else if(a[x]<0) add(x,t,-a[x]); } while(bfs()) ans-=dinic(s,inf); printf("%d ",ans); return 0; }