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 }