BZOJ 1565 NOI2009 植物大战僵尸 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 }

 

posted @ 2018-03-12 21:37  KKKorange  阅读(224)  评论(0编辑  收藏  举报