b2OJ_1565_[NOI2009]植物大战僵尸_拓扑排序+最大权闭合子
题意:n*m个植物,每个植物有分数(可正可负),和能保护植物的位置。只能从右往左吃,并且不能吃正被保护着的,可以一个不吃,求获得的最大分数。
分析:把每个植物向能保护它的植物连边。源点连正权点,负权点连汇点。
考虑在一个环上的植物是吃不到的,我们可以用拓扑排序确定哪些是能吃的。
然后求一遍最大权闭合子图就是答案。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> #include <queue> using namespace std; #define S (n*m+1) #define T (n*m+2) #define P(x,y) (m*(x-1)+y) #define inf 100000000 int head[700],to[1000010],nxt[1000010],flow[1000010],c[1000010],cnt=1,dep[700]; int n,m,can[700],sum,val[700]; void add(int u,int v,int f) { to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;flow[cnt]=f; to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;flow[cnt]=0; } bool bfs() { queue <int> q; memset(dep,0,sizeof(dep)); q.push(S);dep[S]=1; while(q.size()) { int x=q.front();q.pop(); for(int i=head[x];i;i=nxt[i]) { if(!dep[to[i]]&&flow[i]&&can[to[i]]) { dep[to[i]]=dep[x]+1; q.push(to[i]); } } } return dep[T]; } int dfs(int x,int mf) { if(x==T)return mf; int nf=0; for(int i=head[x];i;i=nxt[i]) { if(dep[to[i]]==dep[x]+1&&flow[i]&&can[to[i]]) { int tmp=dfs(to[i],min(flow[i],mf-nf)); nf+=tmp; flow[i]-=tmp; flow[i^1]+=tmp; if(nf==mf)break; } } dep[x]=0; return nf; } void dinic() { int ans=0,f; while(bfs()) { while(f=dfs(S,inf)) { ans+=f; } } printf("%d",sum-ans); } void topsort() { queue <int> q; for(int i=1;i<=T;i++) { if(!c[i])q.push(i); } while(q.size()) { int x=q.front();q.pop(); can[x]=1; if(val[x]>0)sum+=val[x]; for(int i=head[x];i;i=nxt[i]) { c[to[i]]--; if(c[to[i]]==0)q.push(to[i]); } } } int main() { scanf("%d%d",&n,&m); int x,y,z,w; for(int i=1;i<=n*m;i++) { scanf("%d%d",&x,&y); val[i]=x; if(x>0)add(S,i,x),c[S]++; else add(i,T,-x),c[i]++; if(i%m)add(i,i+1,inf),c[i]++; while(y--) { scanf("%d%d",&z,&w); add(P(z+1,w+1),i,inf); c[P(z+1,w+1)]++; } } topsort(); dinic(); } /*************************************************************** Problem: 1580 User: 20170105 Language: C++ Result: Accepted Time:320 ms Memory:16704 kb ****************************************************************/