1. 程式人生 > >BZOJ1565 NOI 2009 植物大戰僵屍 topo+最小割(最大權閉合子圖)

BZOJ1565 NOI 2009 植物大戰僵屍 topo+最小割(最大權閉合子圖)

front 總結 algorithm str ring eof 而是 OS mat

題目鏈接: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 }

BZOJ1565 NOI 2009 植物大戰僵屍 topo+最小割(最大權閉合子圖)