[NOI2009]植物大战僵尸

这道题跟NOI2006 最大获利其实是很像的

一样都是要搞定一些点才能搞定另一些点,然后有些点正权有些点负权

这种问题,其实是最大权闭合子图

amber的最小割论文有详细的讲解法和证明


闭合子图的定义是,图中每个点所连接的的任何一条边不指向图外,可以有边指向这个图

这实际上就是一个依赖关系,如果我们把a依赖b(在这道题就是b保护a),在图中用一条a指向b的边表示

那么我们求的就是一个闭合子图,就是说不可能出现图中选了a而没有选b的情况,因为这样不满足闭合子图的定义

而最大权闭合子图则可以用网络流来解决

对于原图,我们把每条边的流量设为∞

然后对于每个正权点,从st向点连一条边,边权为点的权值

对于每个负权点,从点向ed连一条边,边权为点的权值的绝对值

那么最大权闭合子图的答案就是所有正权-网络流的最小割

因为是最小割,显然我们不会割到原图的流量为∞的边,这样就满足了闭合子图的定义——并没有破坏依赖关系

然后我们如果割了连接st的边,相当于舍弃掉这个正权点,割了连接ed的边,相当于选择了这个负权点

最后所有正权点的权值和(可能达到的最大值)-最小割(付出的代价),就是最大权闭合子图的答案


对于这道题和NOI2006最大获利,一样的建图即可


但是这道题有一点比最大获利要麻烦,就是这道题可以出现环

如果出现了环,求最大权闭合子图的时候则视情况将这个环里的点一并取走或不取

但这道题则要求无论如何都不能取

因此要先toposort一遍去环再建图才行……


这道题我用了递归版的ISAP(主要是可以少写一个BFS),结果暴慢……虽然也能AC……

GZH神犇写了个非递归的ISAP,比我的Dinic快……

好吧以后再也不写递归ISAP了……老老实实写Dinic……


//Lib #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<ctime> #include<iostream> #include<algorithm> #include<vector> #include<string> #include<queue> #include<stack> using namespace std; //Macro #define rep(i,a,b) for(int i=a,tt=b;i<=tt;++i) #define drep(i,a,b) for(int i=a,tt=b;i>=tt;--i) #define erep(i,e,x) for(int i=x;i;i=e[i].next) #define irep(i,x) for(__typedef(x.begin()) i=x.begin();i!=x.end();i++) #define read() (strtol(ipos,&ipos,10)) #define sqr(x) ((x)*(x)) #define pb push_back #define PS system("pause"); typedef long long ll; typedef pair<int,int> pii; const int oo=~0U>>1; const double inf=1e100; const double eps=1e-6; string name="pvz", in=".in", out=".out"; //Var struct E { int next,node,cap; }e[1000008],e2[1000008]; queue<int> q; int n,m,st,ed,tot=1; int cnt[1008],dis[1008],h[1008],h2[1008],node[28][38],map[28][38],ind[1008],ans,size; bool vis[1008]; void add(int a,int b,int c) { e[++tot].next=h[a];e[tot].node=b;e[tot].cap=c;h[a]=tot; e[++tot].next=h[b];e[tot].node=a;e[tot].cap=0;h[b]=tot; } void add2(int a,int b){e2[++tot].next=h2[a];e2[tot].node=b;h2[a]=tot;ind[b]++;} void Toposort() { stack<int> s; int u,v; rep(i,1,n) rep(j,1,m) if(!ind[node[i][j]])s.push(node[i][j]); while(!s.empty()) { u=s.top();s.pop(); vis[u]=true; erep(i,e2,h2[u]) { v=e2[i].node; if((--ind[v])==0)s.push(v); } } } void Build() { tot=1;int v; rep(i,1,n) rep(j,1,m) if(vis[node[i][j]]) { if(map[i][j]>0) add(st,node[i][j],map[i][j]),ans+=map[i][j]; else if(map[i][j]<0)add(node[i][j],ed,-map[i][j]); erep(k,e2,h2[node[i][j]]) if(vis[v=e2[k].node])add(v,node[i][j],oo); } } void Init() { scanf("%d%d",&n,&m); st=n*m+1;ed=n*m+2;int c,t,a,b; rep(i,1,n)rep(j,1,m)node[i][j]=++size; rep(i,1,n) { rep(j,1,m) { scanf("%d%d",&c,&t); map[i][j]=c; rep(k,1,t) { scanf("%d%d",&a,&b);a++;b++; add2(node[i][j],node[a][b]); } if(j!=m)add2(node[i][j+1],node[i][j]); } } Toposort(); Build(); } bool BFS() { memset(dis,-1,sizeof dis); dis[ed]=0;q.push(ed); int u,v;bool flag=false; while(!q.empty()) { u=q.front();q.pop(); erep(i,e,h[u]) { if(dis[v=e[i].node]==-1&&e[i^1].cap) { dis[v]=dis[u]+1; q.push(v); if(v==st)flag=true; } } } return flag; } int DFS(int u,int low) { if(u==ed)return low; int ret=low,tmp,v; erep(i,e,h[u]) { if(!e[i].cap||dis[v=e[i].node]!=dis[u]-1)continue; tmp=DFS(v,min(low,e[i].cap)); e[i].cap-=tmp; e[i^1].cap+=tmp; low-=tmp; if(low==0)break; } if(ret==low)dis[u]=-1; return ret-low; } void Work() { int flow; while(BFS()) while(flow=DFS(st,oo)) ans-=flow; cout<<ans<<endl; } int main() { // freopen((name+in).c_str(),"r",stdin); // freopen((name+out).c_str(),"w",stdout); Init(); Work(); return 0; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值