BZOJ1565 Plants vs. Zombies-Maximum Weight Closed Diagram + Tarjan Condensation

Description

Plants vs. Zombies (PVZ) is a small game that is very popular recently. Plants (plants) and Zombies (zombies) are the protagonists of the game, with Plants defending and Zombies attacking. The game includes a variety of different challenge series, such as Protect Your Brain, Bowling, and more. The most classic of them is that the player controls the Plants to defend the Zombies' attack, or on the contrary, the player controls the Zombies to attack the Plants.
Now, the problem we will consider is the Zombies attack on Plants in the game, please note that the rules in this question are different from the actual game. There are two kinds of characters in the game, Plants and Zombies, each Plant has a set of attack positions, which can protect these positions; the way Zombie attacks the plants is to walk to the positions of the plants and eat them.

The map of the game can be abstracted as a matrix with N rows and M columns, the rows are numbered from 0 to N–1 from top to bottom, and the columns are numbered from 0 to M–1 from left to right; at each position on the map, there are A Plant, for simplicity, we denote the plant at row r and column c as Pr, c.
There are many types of Plants, such as attack, defense and economy. In order to describe each Plant simply, the Score and Attack are defined as follows:

Score[Pr, c]:

Energy available from Zombie defeating plants Pr, c. If Score[Pr, c] is a non-negative integer, it means that defeating plants Pr, c can obtain energy Score[Pr, c], if it is negative, it means that defeating Pr, c requires energy -Score[Pr, c].

Attack [Pr, c]:

The set of locations where plants Pr, c can attack Zombie.
Zombies must be entered from the right side of the map and can only be moved horizontally. The only way for Zombies to attack a plant is to walk up to the plant's location and eat it. So the Zombies attack always starts from the right side of the map. That is to say, for the attack of row r, Zombies must first attack Pr, M-1; if they need to attack Pr, c (0 ≤ c < M-1), they must attack Pr, M-1, Pr, M- 2 … Pr, c+1 must be defeated first and move to position (r, c) before attacking.
In the setting of this question, the attack power of Plants is infinite. Once the Zombie enters the attack position of a Plant, the Zombie will be instantly destroyed, and the Zombie will not have time to perform any attack operations. Therefore, even if the Zombie enters the location of a Plant, but the location belongs to the set of attack locations of other plants, the Zombie will be instantly destroyed and the plants in the location will be safe (in our setting, the attack location of the Plant does not include its own position, otherwise you would not be able to defeat it).

The goal of the Zombies is to attack the Plants positions and get the most energy revenue. Each time, you can choose an attackable plant to attack. The goal of this question is to formulate a set of Zombies attacking plans, choose which plants to attack and the order of attack, so as to obtain the maximum energy income.

The first line of Input
contains two integers N, M, which represent the number of rows and columns of the map, respectively.
The next N×M lines describe information about the plants at each location. Line r×M + c + 1 gives the information of plant Pr, c in the following format:
the first integer is Score[Pr, c], the second integer is the number of positions in the set Attack[Pr, c] w, the
next w position information (r', c'), indicating that Pr, c can attack the position r' row and c' column.
1 ≤ N ≤ 20, 1 ≤ M ≤ 30, -10000 ≤ Score ≤ 10000.

Output
contains only an integer representing the maximum energy income that can be obtained.
Note that you can also choose not to perform any attacks, which will result in 0 energy income.

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

Sample Output
25

//In the example, plant P1,1 can attack position (0,0), P2,0 can attack position (2,1).
One solution is to attack P1,1, P0,1 first, and then attack P0,0.
The total energy gain is (-5)+20+10 = 25.
Note that position (2,1) is protected by plant P2,0, so it cannot attack any plants in row 2.


We can obviously know that if we want to kill the plant (x, y), we must kill (x, j), j∈[y+1,m]. And also have to kill off all the plants that protect it. So obviously, we can build plants on a graph, and then find the maximum weight closed graph of the graph.

What is a closed graph?
A closed graph is a graph (directed graph), and each point in the graph has a weight. For this graph, if you want to choose a point x, you must choose a point that can be connected by x. For example, if there is an x→y edge, if x is selected, y must be selected. If there is an edge from y→x, then if you choose x, you don't have to choose y, but if you choose y, you must choose x.

How to find the maximum weight closed graph?
We can first sum up all the punctual weights in the original image. Then we rebuild the graph and set up a super source point, which is connected to all the positive weight points, and the edge weight is the point weight of the positive weight point; and then set up a super sink point, which is connected to all the negative weight points, and the edge weight is the negative weight point. The absolute value of the point weight of the point. Then the positive weight point is negative and the connection information between the weight points is as in the original image (for example, there is an edge between the positive weight point x and the negative weight point y in the original image, we will connect x→y in the current graph), and Assign the edge weight to INF (extremely large). After the graph is built in this way, the entire graph becomes a network. We find the maximum flow (minimum cut) of the entire graph, subtract the maximum flow from sum, and the remaining value is the value of the maximum weighted closed graph.
You can search for the proof of this method. I will talk about the perceptual understanding here. For a positive weight point, if the final weight of the closed graph connected to it is less than 0, it is definitely not selected. In this way, the absolute value of the negative weight edge connected to it is greater than it, and the minimum cut shown in this way is itself, which is equivalent to subtracting itself, that is, not selecting it. If the final weight is greater than 0, the sum of the absolute values ​​of the negative weight edges is smaller, and the minimum cut shown is the sum of the negative weight edges. Subtracting this is equivalent to selecting a positive weight point and then subtracting the connected negative weight points. .

Let's go back to this question. Obviously, we connect the point (x, y) to the point (x, y+1) when constructing the map, and connect the point protected by (x, y) to (x, y) )superior. Then run out of the maximum weight closed graph of the graph.
But there is a problem here, that is, the graph may have a cycle, what does it mean to have a cycle? We can give an example, say (x, y) protects (x, y+1), then these two points form a ring. If you want to kill (x, y), you must kill (x, y+1) first, but (x, y) protects (x, y+1), so if you want to kill (x, y+1), you must first kill (x, y+1) Kill (x,y). The result of this formation of the ring is that no one can kill it, and both plants are safe.
Therefore, we have to judge the ring first. The points on the ring must be discarded, and the points connected to the points on the ring must be discarded. For example (x,y) is on the ring, (x,y-1) is not on the ring. But (x, y-1) can't be selected either, because it can go from (x, y-1) to (x, y), which is equivalent to (x, y) can't do it, and (x, y-1) can't do it either But (x, y) really can't be killed, so (x, y-1) also has to be discarded. Similarly, if a point on the ring protects point (x, y), then (x, y) must also be discarded.
So we naturally thought of the Tarjan algorithm. We use Tarjan to find each ring, and then process these rings and related points.

Code:

#include<bits/stdc++.h>
#define MAXN 800000
#define MAXM 700
#define INF 2e9
using namespace std;
int read(){
    char c;int x=0,y=1;while(c=getchar(),(c<'0'||c>'9')&&c!='-');
    if(c=='-') y=-1;else x=c-'0';while(c=getchar(),c>='0'&&c<='9')
    x=x*10+c-'0';return x*y;
}
struct node{
    int to,val;
}L[MAXN];
int head[MAXN],nxt[MAXN],dfn[MAXM],low[MAXM],sta[MAXM],vis[MAXM],che[MAXM],dis[MAXM],q[MAXN],val[MAXM];
int cnt,n,m,S,T,top,tim,ans,h,t,tot;
void add(int x,int y,int c){
    L[cnt]=(node){y,c};
    nxt[cnt]=head[x];head[x]=cnt;cnt++;
    L[cnt]=(node){x,0};
    nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
void tarjan(int x){
    low[x]=++tim;dfn[x]=tim;
    sta[++top]=x;vis[x]=1;
    for(int i=head[x];i!=-1;i=nxt[i]){
        if(!L[i].val) continue;
        int to=L[i].to;
        if(to==T) continue;
        if(!dfn[to]) tarjan(to),low[x]=min(low[x],low[to]);
        else if(vis[to]) low[x]=min(low[x],dfn[to]);
    }
    if(low[x]==dfn[x]){
        if(sta[top]==x){
            vis[x]=0;top--;return;
        }
        int now=0;
        while(now!=x){
            now=sta[top];top--;vis[now]=0;
            if(val[now]>0&&!che[now]) tot-=val[now];che[now]=1;
        }
    }
}
int BFS(){
    memset(dis,0,sizeof(dis));dis[S]=1;
    h=t=0;q[++t]=S;
    while(h<t){
        int front=q[++h];
        for(int i=head[front];i!=-1;i=nxt[i]){
            int to=L[i].to;
            if(L[i].val&&!dis[to]&&!che[to]){
                q[++t]=to;
                dis[to]=dis[front]+1;
            }
        }
    }
    return dis[T];
}
int DFS(int now,int x){
    if(now==T) return x;
    int res=0;
    for(int i=head[now];i!=-1&&x;i=nxt[i]){
        int to=L[i].to;
        if(dis[to]==dis[now]+1&&L[i].val&&!che[to]){
            int fd=DFS(to,min(x,L[i].val));
            x-=fd;res+=fd;
            L[i].val-=fd;L[i^1].val+=fd;
        }
    }
    if(!res) dis[now]=-1;
    return res;
}
void dfs(int x){
    for(int i=head[x];i!=-1;i=nxt[i]){
        int to=L[i].to;
        if(!L[i].val&&!che[to]){
            che[to]=1;if(val[to]>0) tot-=val[to];
            dfs(to);
        }
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    n=read();m=read();S=0;T=n*m+1;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++){
        int x=read(),w=read(),pl=(i-1)*m+j;val[pl]=x;
        for(int i=1;i<=w;i++){
            int px=read(),py=read();
            if((px)*m+py+1==pl&&!che[pl]){
                che[pl]=1;if(val[pl]>0) tot-=val[pl];
             }
            add((px)*m+py+1,pl,INF);
         }
        if(x>=0) add(S,pl,x),tot+=x;
        else add(pl,T,-x); 
     }
    for(int i=1;i<=n*m;i++)
     if(i%m!=1) add(i-1,i,INF);
    for(int i=1;i<=n*m;i++)
      if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n*m;i++)
      if(che[i]) dfs(i);
    while(BFS()) ans+=DFS(S,2e9);
    printf("%d",tot-ans);
    return 0;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325565629&siteId=291194637