BZOJ1565 NOI 2009 植物大战僵尸 topo+最小割(最大权闭合子图)

题目链接:https://www.luogu.org/problemnew/show/P2805(bzoj那个实在是有点小小的辣眼睛。。。我就把洛谷的丢出来吧。。。)

题意概述:给出一张有向图,这张有向图上的每个点都有一个点权,想要访问某个点必须要先访问这个点所能够访问(遍历)到的所有点,在访问到一个点之后将会得到这个点的权值(可正可负)。问访问这张图可以得到的最大点权和。

原题说过来说过去实际上是描述了一个植物之间的保护关系,也就是说明了植物之间的先后访问顺序之间的关系。可以描述为要“要访问点a,先要访问点b”这样的形式,并且题意总结出来之后很容易发现这就是一个最大权闭合子图问题。

但是我们注意到原题给出的并不一定是一个DAG图。事实上,可能有些植物互相保护,导致僵尸只能当炮灰。。。。于是我们需要把环去掉,这一步可以topo序解决,只是不是真的topo,而是从入度为0的点开始的(访问所有可以不经过环就可以访问到的点)。

然后就是直接网络流上跑最大权闭合子图。但是值得注意的是最大权闭合子图是满足一种推导关系,即上面的“要访问点a,先要访问先b”这种关系形成的一条有向边,容量为inf,因为要满足这样的推导关系,所以上面求topo的时候连的边要反过来。然后原点向所有点权为正的边连一条边,容量为点权;所有点权为负的点向汇点连一条边,容量为点权的相反数。在求最小割的时候如果一条边被割掉,那么意义就是做出选择(S有关的边是不选这个点,T有关的边是选了这个点)之后付出的代价。

最后只需要把所有的正权点权值相加(可以得到的最大收益),减去最小割(付出的最小代价),就是我们最终获得的最大收益。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<set>
  9 #include<map>
 10 #include<vector>
 11 #include<cctype>
 12 using namespace std;
 13 const int MAXN=25;
 14 const int MAXM=35;
 15
 16 int N,M,sco[MAXN][MAXM];
 17 struct XY{ int x,y; };
 18 vector<XY>att[MAXN][MAXM];
 19 struct graph{
 20     static const int maxn=605;
 21     static const int maxm=360050;
 22     struct edge{ int from,to,next; }E[maxm];
 23     int n,first[maxn],np,rd[maxn],topo[maxn];
 24     graph(){ np=0; }
 25     void add_edge(int u,int v){
 26         E[++np]=(edge){u,v,first[u]};
 27         first[u]=np;
 28     }
 29     void topo_sort(){
 30         queue<int>q;
 31         for(int i=1;i<=n;i++)
 32             if(!rd[i]) topo[i]=1,q.push(i);
 33         while(!q.empty()){
 34             int i=q.front(); q.pop();
 35             for(int p=first[i];p;p=E[p].next){
 36                 int j=E[p].to;
 37                 if(--rd[j]==0) topo[j]=1,q.push(j);
 38             }
 39         }
 40     }
 41 }gp;
 42 struct NET{
 43     static const int maxn=605;
 44     static const int maxm=360050;
 45     static const int inf=1e7+5;
 46     struct edge{ int from,to,next,cap,flow; }E[maxm<<1];
 47     int S,T,n,first[maxn],np,fl[maxn],gap[maxn],d[maxn],cur[maxn];
 48     NET(){ np=0; }
 49     void add_edge(int u,int v,int w){
 50         E[++np]=(edge){u,v,first[u],w,0};
 51         first[u]=np;
 52         E[++np]=(edge){v,u,first[v],0,0};
 53         first[v]=np;
 54     }
 55     void BFS(){
 56         queue<int>q;
 57         for(int i=1;i<=n;i++) d[i]=n;
 58         d[T]=0; q.push(T);
 59         while(!q.empty()){
 60             int i=q.front(); q.pop();
 61             for(int p=first[i];p;p=E[p].next){
 62                 int j=E[p].to,pp=(p-1^1)+1;
 63                 if(E[pp].cap>E[pp].flow&&d[j]==n) d[j]=d[i]+1,q.push(j);
 64             }
 65         }
 66     }
 67     int augment(){
 68         int now=T,flow=inf;
 69         while(now!=S){
 70             flow=min(flow,E[fl[now]].cap-E[fl[now]].flow);
 71             now=E[fl[now]].from;
 72         }
 73         now=T;
 74         while(now!=S){
 75             E[fl[now]].flow+=flow,E[(fl[now]-1^1)+1].flow-=flow;
 76             now=E[fl[now]].from;
 77         }
 78         return flow;
 79     }
 80     int ISAP(){
 81         memcpy(cur,first,sizeof(first));
 82         BFS();
 83         for(int i=1;i<=n;i++) gap[d[i]]++;
 84         int now=S,flow=0;
 85         while(d[S]<n){
 86             if(now==T) flow+=augment(),now=S;
 87             bool ok=0;
 88             for(int p=cur[now];p;p=E[p].next){
 89                 int j=E[p].to;
 90                 if(E[p].cap>E[p].flow&&d[j]+1==d[now]){
 91                     ok=1,fl[j]=cur[now]=p,now=j;
 92                     break;
 93                 }
 94             }
 95             if(!ok){
 96                 int minl=n;
 97                 for(int p=first[now];p;p=E[p].next){
 98                     int j=E[p].to;
 99                     if(E[p].cap>E[p].flow&&d[j]+1<minl) minl=d[j]+1;
100                 }
101                 if(--gap[d[now]]==0) break;
102                 gap[d[now]=minl]++;
103                 cur[now]=first[now];
104                 if(now!=S) now=E[fl[now]].from;
105             }
106         }
107         return flow;
108     }
109 }net;
110
111 void data_in()
112 {
113     scanf("%d%d",&N,&M);
114     int w,x,y;
115     for(int i=1;i<=N;i++)
116     for(int j=1;j<=M;j++){
117         scanf("%d%d",&sco[i][j],&w);
118         for(int k=1;k<=w;k++){
119             scanf("%d%d",&x,&y);
120             att[i][j].push_back((XY){x+1,y+1});
121         }
122     }
123 }
124 int idx(int x,int y){ return (x-1)*M+y; }
125 void build_net()
126 {
127     for(int i=1;i<=N;i++)
128     for(int j=1;j<=M;j++){
129         int id=idx(i,j);
130         for(int k=1;k+j<=M;k++){
131             gp.add_edge(id+k,id);
132             gp.rd[id]++;
133         }
134         for(int k=0;k<att[i][j].size();k++){
135             int _id=idx(att[i][j][k].x,att[i][j][k].y);
136             gp.add_edge(id,_id); gp.rd[_id]++;
137         }
138     }
139     gp.n=N*M; gp.topo_sort();
140     for(int p=1;p<=gp.np;p++){
141         int x=gp.E[p].from,y=gp.E[p].to;
142         if(gp.topo[x]&&gp.topo[y]) net.add_edge(y,x,net.inf);
143     }
144     net.n=N*M+2,net.S=net.n-1,net.T=net.n;
145     for(int i=1;i<=N;i++)
146     for(int j=1;j<=M;j++) if(gp.topo[idx(i,j)]){
147         if(sco[i][j]>0) net.add_edge(net.S,idx(i,j),sco[i][j]);
148         else if(sco[i][j]<0) net.add_edge(idx(i,j),net.T,-sco[i][j]);
149     }
150 }
151 void work()
152 {
153     build_net();
154     int sum=0;
155     for(int i=1;i<=N;i++)
156     for(int j=1;j<=M;j++)
157         if(sco[i][j]>0&&gp.topo[idx(i,j)]) sum+=sco[i][j];
158     printf("%d\n",sum-net.ISAP());
159 }
160 int main()
161 {
162     data_in();
163     work();
164     return 0;
165 }

原文地址:https://www.cnblogs.com/KKKorange/p/8552121.html

时间: 2024-10-05 21:15:20

BZOJ1565 NOI 2009 植物大战僵尸 topo+最小割(最大权闭合子图)的相关文章

b2OJ_1565_[NOI2009]植物大战僵尸_拓扑排序+最大权闭合子图

b2OJ_1565_[NOI2009]植物大战僵尸_拓扑排序+最大权闭合子 题意:n*m个植物,每个植物有分数(可正可负),和能保护植物的位置.只能从右往左吃,并且不能吃正被保护着的,可以一个不吃,求获得的最大分数. 分析:把每个植物向能保护它的植物连边.源点连正权点,负权点连汇点. 考虑在一个环上的植物是吃不到的,我们可以用拓扑排序确定哪些是能吃的. 然后求一遍最大权闭合子图就是答案. 代码: 1 #include <stdio.h> 2 #include <string.h>

HDU 4971 A simple brute force problem.(最小割---最大权闭合)

题目地址:HDU 4971 比赛的时候还不会最大权闭合,当时还跟队友讨论了好长时间费用流和DP..现在看来就是一最大权闭合水题... 建图思路:源点连工程,权值为利润,汇点连科技项目,权值为花费,然后对有依赖性的连有向边.用正权值和减去最小割就是答案. #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #in

HDU 3061 Battle(最小割----最大权闭合图)

题目地址:HDU 3061 多校中遇到的最小割的最大权闭合模型花了一上午时间终于看懂啦. 最大权闭合图就是将一些互相有依赖关系的点转换成图,闭合图指的是在图中的每一个点的后继点都是在图内的. 还要明白简单割的概念,就是指所有的割边都与源点或汇点相连.然后让源点与正权点相连,汇点与负权点相连,权值均为其绝对值,有依赖关系的点连一条有向边,如果a必须在b的基础上,那么就连一条a->b的有向边,权值为INF.最后用所有正权值得和减去最小割的值就是答案. 具体证明可看胡伯涛大牛的国家队集训论文<最小割

[BZOJ 1565][NOI 2009]植物大战僵尸(Dinic最大流+拓扑排序)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1565 orz题目太神...膝盖已经跪烂.... 这题需要我们建立起植物和植物之间复杂的保护关系,以及吃植物得到的损失和回报,因此要用最大流搞,建模太神奇了,跪跪跪... 首先我们建立源点和汇点,对于每个植物,如果吃它可以得到能源,在源点和它之间连一条边,容量为得到的能源数量.如果吃它要消耗能源,则在它和汇点之间建立一条边,边权为消耗的能源个数. 然后对于每个植物,将它和它的保护区

【BZOJ-3438】小M的作物 最小割 + 最大权闭合图

3438: 小M的作物 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 825  Solved: 368[Submit][Status][Discuss] Description 小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子有1个(就是可以种一棵作物)(用1...n编号),现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植可以获得bi的收益,而且,现在还有这么一种神奇的现象,就

HDU 3917 Road constructions(最小割---最大权闭合)

题目地址:HDU 3917 这题简直神题意...题目本身就很难看懂不说..即使看懂了,也对这题意的逻辑感到无语...不管了..就按照那题意上说的做吧... 题意:给你n个城市,m个公司,若干条可能要建设的道路,每条有向道路需要花费,还有负责建设这条道路的公司编号,如果要建设ab道路,那么负责这条道路的公司需要建设完它负责的所有道路,而且对负责道路bx(x任意)的公司同样要求,以此类推. 每个公司还要交税,问最多能收多少税. 按照题意来的话,相当于某些公司对某些公司有依赖性.最终的利润==收的税-

HDU 3996 Gold Mine(最小割---最大权闭合)

题目地址:HDU 3996 很简单的最大权闭合题,只不过有个小小的坑点..那就是需要用int64....但是为什么我交上去返回的TLE.... 建图思路:分别对每一层的金矿都编号.方便处理.然后源点连接正权点,汇点连接负权点,每个点连接它所依赖的. 用正权之和减去最大流即为答案. 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include

HDU 3879 Base Station(最小割---最大权闭合)

题目地址:HDU 3879 无语...对这题的数据范围无语..刚上来一看,建图思路很快出来了,但是一看数据范围..需要5w个点..于是我以为需要缩点或是别的优化..于是又想了会怎么优化,感觉没法优化了..于是上网一搜..还真都是就这么过了...过了..过了..于是,我就按刚上来那个思路敲,结果203ms过了...我真怀疑出题者是不是不小心把数据范围多写了几个0.这要放在比赛中..肯定不敢这么写... 建图思路是,对所有任务额外建一个点,并连源点,权值为任务的获利.然后将每个任务所对应的站点连边,

hiho一下 第119周 #1398 : 网络流五&#183;最大权闭合子图 【最小割-最大流--Ford-Fulkerson 与 Dinic 算法】

#1398 : 网络流五·最大权闭合子图 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 周末,小Hi和小Ho所在的班级决定举行一些班级建设活动. 根据周内的调查结果,小Hi和小Ho一共列出了N项不同的活动(编号1..N),第i项活动能够产生a[i]的活跃值. 班级一共有M名学生(编号1..M),邀请编号为i的同学来参加班级建设活动需要消耗b[i]的活跃值. 每项活动都需要某些学生在场才能够进行,若其中有任意一个学生没有被邀请,这项活动就没有办法进行. 班级建设的活