NOI2009 植物大战僵尸
题解:
由于干每棵植物之前需要先干掉它右面的植物和保护他的植物,
我们可以发现这是最大权闭合子图问题。
简单提一下。
闭合子图,指这个子图中所有的点只会指向子图中的点。
最大权,指这些点有点权,要求得到的闭合子图点权之和最大。
解决办法是,$S$向正点权的点连容量为点权的边,负点权的点向$T$连容量为点权相反数的边。
然后正点权之和-得到的最大流即为最大权。
然而这道题还有种情况,就是自己守自己。
这种情况会构成环,所以拓扑/$Tarjan$除环。
代码:
#include<queue> #include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 650 #define ll long long const int inf = 0x3f3f3f3f; const ll Inf = 0x3f3f3f3f3f3f3f3fll; inline int rd() { int f=1,c=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();} return f*c; } int n,m,hed[N],cnt=-1,S=0,T=601,v[N],ind[N]; vector<int>ve[N]; int _id(int x,int y) { return x*m+y+1; } struct EG { int to,nxt; ll w; }e[N*N*10]; void ae(int f,int t,ll w) { e[++cnt].to = t; e[cnt].nxt = hed[f]; e[cnt].w = w; hed[f] = cnt; } void AE(int f,int t,ll w) { ae(f,t,w); ae(t,f,0); } queue<int>q; bool vis[N]; ll sum; int dep[N],cur[N]; bool bfs() { memset(dep,0x3f,sizeof(dep)); memcpy(cur,hed,sizeof(cur)); dep[S]=0,vis[S]=1;q.push(S); while(!q.empty()) { int u = q.front(); q.pop(); for(int j=hed[u];~j;j=e[j].nxt) { int to = e[j].to; if(e[j].w&&dep[to]>dep[u]+1) { dep[to] = dep[u]+1; if(!vis[to]) { vis[to] = 1; q.push(to); } } } vis[u] = 0; } return dep[T]!=inf; } ll dfs(int u,ll lim) { if(u==T||!lim)return lim; ll fl=0,f; for(int j=cur[u];~j;j=e[j].nxt) { cur[u] = j; int to = e[j].to; if(dep[to]==dep[u]+1&&(f=dfs(to,min(lim,e[j].w)))) { fl+=f,lim-=f; e[j].w-=f,e[j^1].w+=f; if(!lim)break; } } return fl; } ll dinic() { ll ret = 0; while(bfs())ret+=dfs(S,Inf); return ret; } int main() { n = rd(),m = rd(); memset(hed,-1,sizeof(hed)); for(int i=0;i<n;i++) for(int s,x,y,j=0;j<m;j++) { int u = _id(i,j); v[u] = rd(); s = rd(); while(s--) { x = rd(),y = rd(); int to = _id(x,y); ind[to]++; ve[u].push_back(to); } if(j!=0) { ind[u-1]++; ve[u].push_back(u-1); } } for(int i=m;i<=n*m;i+=m) if(!ind[i])q.push(i); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 1; if(v[u]>0)sum+=v[u]; for(int i=0;i<ve[u].size();i++) { int to = ve[u][i]; ind[to]--; if(!ind[to]) q.push(to); } } for(int i=1;i<=n*m;i++) { if(vis[i]) { if(v[i]>0)AE(S,i,v[i]); if(v[i]<0)AE(i,T,-v[i]); for(int j=0;j<ve[i].size();j++) { int to = ve[i][j]; if(vis[to])AE(to,i,Inf); } } } memset(vis,0,sizeof(vis)); printf("%lld\n",sum-dinic()); return 0; }