描述
http://www.lydsy.com/JudgeOnline/problem.php?id=1565
n*m的矩阵,可以种植植物,僵尸从图的右边进入吃植物.前面的植物可以保护后面的植物,还有一些植物会保护特定位置的其他植物.这里保护的意思是:a保护b等价于僵尸必须先吃a,才能吃b.每个植物有一个价值(可正可负),问僵尸能获得的最大价值.
1565: [NOI2009]植物大战僵尸
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1972 Solved: 917
[Submit][Status][Discuss]
Description
![](http://www.lydsy.com/JudgeOnline/images/1565_1.jpg)
Input
![](http://www.lydsy.com/JudgeOnline/images/1565_2.jpg)
Output
Sample Input
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0
Sample Output
HINT
在样例中, 植物P1,1可以攻击位置(0,0), P2, 0可以攻击位置(2,1)。
一个方案为,首先进攻P1,1, P0,1,此时可以攻击P0,0 。共得到能源收益为(-5)+20+10 = 25。注意, 位置(2,1)被植物P2,0保护,所以无法攻击第2行中的任何植物。
【大致数据规模】
约20%的数据满足1 ≤ N, M ≤ 5;
约40%的数据满足1 ≤ N, M ≤ 10;
约100%的数据满足1 ≤ N ≤ 20,1 ≤ M ≤ 30,-10000 ≤ Score ≤ 10000 。
Source
分析
要吃一个植物必须要吃保护它的植物,很容易想到最大权闭合图,由被保护植物向保护它的植物连边.但此题特殊的地方在于可能形成环,也就是要吃掉这个植物必须先吃掉他自己,显然这是不可能的,所以我们可以用Tarjan先去掉强连通分量,然后递归把所有不能吃的植物保护的植物都标记掉.标记结束后把剩下能吃的植物与源点汇点,以及在彼此之间连边,跑最大流即可.
注意:
1.Dinic的dfs里回退建立反向弧的时候好几次都把g[e.to][e.rev]+=d写成了-=d.....查了一个小时都没发现.
ps.关于Tarjan.
1.对于图中的一个强连通分量,进入第一个点后,在回退出来之前,可以搜索到所有其他点,所以一个强连通分量一定在同一棵搜索子树中,并且如果在搜到的时候入栈的话,所有其他点都在第一个点的上面.对于极大的强连通分量,第一个点不可能的连到时间戳更小的点(因为当前已经极大,如果可以,时间戳更小的点一定能连到时间戳更大的点,则当前强连通分量就不是极大的了),所以第一个点能连到的最早的点是自己,而之后的强连通分量中的其他点都可以连到更早的点,更早的点可以连到更早更早的点,最后都能连到第一个点.当我们做Tarjan算法的时候,当遇到dfn==low的点,把栈中所有它上面的点都弹出,也就是清空以这个点为根的子树.根据这样的规则,当遇到dfn==low的点的时候,由于这个点的子节点都访问过了,也就是再次回退到了这个点,所以这个点能到达的最早的点是所有他的子节点和他自己能到达的最早的点中最小的,这样就可以知道以这个点为根的子树中的所有点能到的最早的点都不会早于这个点.所以这个点与比它早的点不连通.而此时子树中无法到达当前点的子树都已经弹出栈了,也就是栈中当前点之上的点都可以到达当前点,那么这些点就强连通,并且无法与更早或更晚的点再连通,所以是极大的.
2.关于时间戳.我之前一直写的是把时间戳作为dfs的参数,也就是某点x的时间戳为idx,则它的所有子节点都是idx+1,这样写对不对?
是错误的!
当找到dfn==low时,栈上方的点都可以追溯到当前点的时间戳,如果就是当前点,大丈夫.如果是其他与当前点同一级的点,那个点还没有出栈,说明它能到更早的地方,然后通过更早的地方就能到当前点,这样仍是强连通分量,大丈夫.但是此时我们只统计了当前点的子树,扔掉了帮助过我们的那个和当前点同一级的点,这样强连通分量就不是极大的了.比如 1->2, 2->3, 3->1, 1->4, 4->2. 如果按我之前的写法就会认为1,2,3强连通,而4与自己强连通,但实际上这四个点都是强连通的.
不过!但是!
如果把
else if(instack[u]) { low[v]=min(low[v],dfn[u]); }
改成
else if(instack[u]) { low[v]=min(low[v],low[u]); }
就又正确了.为什么呢?
之前的写法是追溯到当前点为根的子树所能直接到达的点,也就是说如果不是当前点x直接到达的,就是它的子节点直接到达的,或者子节点的子节点...总之是这个子树中某个点直接到达的.这就会出现之前所说的错误,导致强连通分量不是极大的.但如果改成第二种写法,那当前点的low就是子树中的点能够直接或间接到达的最早的点,也就是一下找到头了.这样一来,找到dfn==low的时候,同一时间戳的其他点还存在,说明这些点还能到更早的点,那连向这些点的low值应该更早(一下找到最早最早),而现在栈中的点最早能到达当前点的时间戳,而不是更早,说明这些点连的不是其他点而是当前点,所以强连通分量一定是极大的.或者可以这样理解,最早只能到当前点的时间戳,子树的外面是强连通的(已经处理完了),无法跑到子树的上面也就是无法跑到子树的外面去,所以只能在子树内强连通.
感觉以后还是写得规范一点吧.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<stack> 5 #include<queue> 6 #include<vector> 7 #include<iostream> 8 #define rep(i,n) for(int i=0;i<(n);i++) 9 #define for1(i,a,n) for(int i=(a);i<=(n);i++) 10 #define read(a) a=getnum() 11 #define CC(i,a) memset(i,a,sizeof(i)) 12 using namespace std; 13 14 const int maxn=(20*30)+5,INF=0x7fffffff; 15 int n,m,W,N; 16 int w[maxn]; 17 bool mark[maxn]; 18 vector <int> e[maxn]; 19 20 inline int getnum() { int r=0,k=1;char c;for(c=getchar();c<'0'||c>'9';c=getchar()) if(c=='-') k=-1; for(;c>='0'&&c<='9';c=getchar()) r=r*10+c-'0'; return r*k; } 21 22 struct Tarjan 23 { 24 int idx; 25 int dfn[maxn],low[maxn]; 26 bool ins[maxn]; 27 stack <int> s; 28 29 void dfs(int u) 30 { 31 s.push(u); 32 ins[u]=true; 33 dfn[u]=low[u]=idx++; 34 rep(i,e[u].size()) 35 { 36 int v=e[u][i]; 37 if(!dfn[v]) 38 { 39 dfs(v); 40 low[u]=min(low[u],low[v]); 41 } 42 else if(ins[v]) 43 { 44 low[u]=min(low[u],dfn[v]); 45 } 46 } 47 if(low[u]==dfn[u]) 48 { 49 ins[u]=false; 50 if(s.top()==u) s.pop(); 51 else 52 { 53 int top; 54 while((top=s.top())!=u) 55 { 56 s.pop(); 57 ins[top]=false; 58 mark[top]=true; 59 } 60 s.pop(); 61 mark[top]=true; 62 } 63 } 64 } 65 66 }tarjan; 67 68 struct Flow 69 { 70 int level[maxn],iter[maxn]; 71 struct edge 72 { 73 int to,cap,rev; 74 edge() {} 75 edge(int a,int b,int c):to(a),cap(b),rev(c) {} 76 }; 77 vector <edge> g[maxn]; 78 79 void add_edge(int from,int to,int cap) 80 { 81 g[from].push_back(edge(to,cap,g[to].size())); 82 g[to].push_back(edge(from,0,g[from].size()-1)); 83 } 84 85 void bfs(int s) 86 { 87 CC(level,-1); 88 level[s]=0; 89 queue <int> q; 90 q.push(s); 91 while(!q.empty()) 92 { 93 int t=q.front(); q.pop(); 94 rep(i,g[t].size()) 95 { 96 edge e=g[t][i]; 97 if(e.cap>0&&level[e.to]<0) 98 { 99 level[e.to]=level[t]+1; 100 q.push(e.to); 101 } 102 } 103 } 104 } 105 106 int dfs(int u,int t,int f) 107 { 108 if(u==t) return f; 109 for(int &i=iter[u];i<g[u].size();i++) 110 { 111 edge &e=g[u][i]; 112 if(e.cap>0&&level[e.to]>level[u]) 113 { 114 int d=dfs(e.to,t,min(f,e.cap)); 115 if(d>0) 116 { 117 e.cap-=d; 118 g[e.to][e.rev].cap+=d; 119 return d; 120 } 121 } 122 } 123 return 0; 124 } 125 126 int max_flow(int s,int t) 127 { 128 int flow=0; 129 bfs(s); 130 while(level[t]>0) 131 { 132 int f; 133 CC(iter,0); 134 while((f=dfs(s,t,INF))>0) flow+=f; 135 bfs(s); 136 } 137 return flow; 138 } 139 140 }flow; 141 142 void pro(int u) 143 { 144 if(mark[u]) return; 145 mark[u]=true; 146 rep(i,e[u].size()) 147 { 148 pro(e[u][i]); 149 } 150 } 151 152 void solve() 153 { 154 for1(i,1,N) 155 { 156 if(!tarjan.dfn[i]) 157 { 158 tarjan.dfs(i); 159 } 160 } 161 for1(i,1,N) 162 { 163 if(mark[i]) 164 { 165 rep(j,e[i].size()) 166 { 167 pro(e[i][j]); 168 } 169 } 170 } 171 for1(i,1,N) 172 { 173 if(mark[i]) continue; 174 if(w[i]>0) 175 { 176 W+=w[i]; 177 flow.add_edge(0,i,w[i]); 178 } 179 else 180 { 181 flow.add_edge(i,N+1,-w[i]); 182 } 183 rep(j,e[i].size()) 184 { 185 flow.add_edge(e[i][j],i,INF); 186 } 187 } 188 printf("%d ",W-flow.max_flow(0,N+1)); 189 } 190 191 void init() 192 { 193 read(n); read(m); N=n*m; 194 for1(i,1,n) 195 { 196 for1(j,1,m) 197 { 198 int u=(i-1)*m+j,a; 199 read(w[u]); read(a); 200 while(a--) 201 { 202 int x,y; 203 read(x); read(y); 204 int v=x*m+y+1; 205 e[u].push_back(v); 206 } 207 if(j>1) 208 { 209 e[u].push_back(u-1); 210 } 211 } 212 } 213 } 214 215 int main() 216 { 217 #ifndef ONLINE_JUDGE 218 freopen("plant.in","r",stdin); 219 freopen("plant.out","w",stdout); 220 #endif 221 init(); 222 solve(); 223 #ifndef ONLINE_JUDGE 224 fclose(stdin); 225 fclose(stdout); 226 system("plant.out"); 227 #endif 228 return 0; 229 }