BZOJ_1565_[NOI2009]_植物大战僵尸_(Tarjan+最大流+最大权闭合图)

描述


http://www.lydsy.com/JudgeOnline/problem.php?id=1565

n*m的矩阵,可以种植植物,僵尸从图的右边进入吃植物.前面的植物可以保护后面的植物,还有一些植物会保护特定位置的其他植物.这里保护的意思是:a保护b等价于僵尸必须先吃a,才能吃b.每个植物有一个价值(可正可负),问僵尸能获得的最大价值.

 

1565: [NOI2009]植物大战僵尸

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1972  Solved: 917
[Submit][Status][Discuss]

Description

Input

Output

仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。

Sample Input

3 2
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0

Sample Output

25

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值应该更早(一下找到最早最早),而现在栈中的点最早能到达当前点的时间戳,而不是更早,说明这些点连的不是其他点而是当前点,所以强连通分量一定是极大的.或者可以这样理解,最早只能到当前点的时间戳,子树的外面是强连通的(已经处理完了),无法跑到子树的上面也就是无法跑到子树的外面去,所以只能在子树内强连通.

感觉以后还是写得规范一点吧.

  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\n",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 }
View Code

 

posted @ 2016-04-28 09:48  晴歌。  阅读(828)  评论(0)    收藏  举报