传送门 这道题吃到某个植物a可能需要先吃掉别的植物b(在他的右边或者保护着他),那么我们把a连向b。 发现这是最大权闭合子图。 显然是可以通过网络流水过的。 闭合子图: V中顶点的所有出边均指向V内部顶点 那么按照最大权闭合图的建图方法: 1.s向正权点连流量为权值的边 2.负权点向t连流量为权值的绝对值的边 3.有边相连的两点连流量为inf的边 答案就是正权点的权值总和减去最小割。 但是数据中可能会出现氪金植物(玩家) 他们之间的保护关系像一个环(想象一个无冷却的食人花前面放一个坚果) 显然非氪金玩家僵尸是干不掉他们的。 所以在跑网络流之前先进行拓扑排序。 强制删除不可达点即可。
#include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<iostream> #include<algorithm> #define inf 0x3f3f3f3f #define N 605 using namespace std; struct edge{int to,f,next;}e[500005]; int head[N],in[N],dis[N],f[N],q[N],ok[N]; int n,m,S,T,p,x,y,ans,tot=1; void ins(int x,int y,int w){ e[++tot].to=y; e[tot].f=w; e[tot].next=head[x]; head[x]=tot; e[++tot].to=x; e[tot].f=0; e[tot].next=head[y]; head[y]=tot; in[x]++; } void topo(){ int h=0,t=0,x; for (int i=1;i<=n*m+2;i++) if (!in[i]) q[++t]=i; while (h<t){ x=q[++h]; ok[x]=1; if (f[x]>0) ans+=f[x]; for (int i=head[x];i;i=e[i].next) if (i&1){ in[e[i].to]--; if (!in[e[i].to]) q[++t]=e[i].to; } } } bool bfs(){ int h=0,t=1,x; memset(dis,-1,sizeof(dis)); q[1]=S; dis[S]=0; while (h<t){ x=q[++h]; for (int i=head[x];i;i=e[i].next) if (dis[e[i].to]==-1&&e[i].f&&ok[e[i].to]){ dis[e[i].to]=dis[x]+1; q[++t]=e[i].to; } } return dis[T]!=-1; } int dinic(int x,int flow){ if (x==T) return flow; int rest=flow,k; for (int i=head[x];i&&rest;i=e[i].next) if (e[i].f&&dis[x]+1==dis[e[i].to]){ k=dinic(e[i].to,min(rest,e[i].f)); rest-=k; e[i].f-=k; e[i^1].f+=k; } if (rest) dis[x]=-1; return flow-rest; } int main(){ scanf("%d%d",&n,&m); S=n*m+1; T=S+1; for (int i=1;i<=n*m;i++){ scanf("%d",&f[i]); if (f[i]>0) ins(S,i,f[i]); else ins(i,T,-f[i]); scanf("%d",&p); for (int j=1;j<=p;j++){ scanf("%d%d",&x,&y); ins(x*m+y+1,i,inf); } if (i%m) ins(i,i+1,inf); } topo(); while (bfs()) ans-=dinic(S,inf); printf("%d",ans); }