【最大权闭合子图 tarjan】bzoj1565: [NOI2009]植物大战僵尸

dinic+tarjan板子练手题

Description

Plants vs. Zombies(PVZ)是最近十分风靡的一款小游戏。Plants(植物)和Zombies(僵尸)是游戏的主角,其
中Plants防守,而Zombies进攻。该款游戏包含多种不同的挑战系列,比如Protect Your Brain、Bowling等等。其
中最为经典的,莫过于玩家通过控制Plants来防守Zombies的进攻,或者相反地由玩家通过控制Zombies对Plants发
起进攻。
 
现在,我们将要考虑的问题是游戏中Zombies对Plants的进攻,请注意,本题中规则与实际游戏有所不同。游戏中
有两种角色,Plants和Zombies,每个Plant有一个攻击位置集合,它可以对这些位置进行保护;而Zombie进攻植物
的方式是走到植物所在的位置上并将其吃掉。
 
游戏的地图可以抽象为一个N行M列的矩阵,行从上到下用0到N–1编号,列从左到右用0到M–1编号;在地图的每个
位置上都放有一个Plant,为简单起见,我们把位于第r行第c列的植物记为Pr, c。
 
Plants分很多种,有攻击类、防守类和经济类等等。为了简单的描述每个Plant,定义Score和Attack如下:
 
Score[Pr, c]
 
Zombie击溃植物Pr, c可获得的能源。若Score[Pr, c]为非负整数,则表示击溃植物Pr, c可获得能源Score[Pr, c]
,若为负数表示击溃Pr, c需要付出能源 -Score[Pr, c]。
 
Attack[Pr, c]
 
植物Pr, c能够对Zombie进行攻击的位置集合。
 
Zombies必须从地图的右侧进入,且只能沿着水平方向进行移动。Zombies攻击植物的唯一方式就是走到该植物所在
的位置并将植物吃掉。因此Zombies的进攻总是从地图的右侧开始。也就是说,对于第r行的进攻,Zombies必须首
先攻击Pr, M-1;若需要对Pr, c(0 ≤ c < M-1)攻击,必须将Pr,M-1, Pr, M-2 … Pr, c+1先击溃,并移动到位
置(r, c)才可进行攻击。
 
在本题的设定中,Plants的攻击力是无穷大的,一旦Zombie进入某个Plant的攻击位置,该Zombie会被瞬间消灭,
而该Zombie没有时间进行任何攻击操作。因此,即便Zombie进入了一个Plant所在的位置,但该位置属于其他植物
的攻击位置集合,则Zombie会被瞬间消灭而所在位置的植物则安然无恙(在我们的设定中,Plant的攻击位置不包
含自身所在位置,否则你就不可能击溃它了)。
 
Zombies的目标是对Plants的阵地发起进攻并获得最大的能源收入。每一次,你可以选择一个可进攻的植物进行攻
击。本题的目标为,制定一套Zombies的进攻方案,选择进攻哪些植物以及进攻的顺序,从而获得最大的能源收入

Input

第一行包含两个整数N, M,分别表示地图的行数和列数。
接下来N×M行描述每个位置上植物的信息。第r×M + c + 1行按照如下格式给出植物Pr, c的信息:
第一个整数为Score[Pr, c], 第二个整数为集合Attack[Pr, c]中的位置个数w,
接下来w个位置信息(r’, c’),表示Pr, c可以攻击位置第r’ 行第c’ 列。
1 ≤ N ≤ 20,1 ≤ M ≤ 30,-10000 ≤ Score ≤ 10000 。
 

Output

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

题目大意

每种植物有若干个可保护的点,且僵尸消灭每种植物都会有特定权值。僵尸必须从右边进入,依次消灭没有被任何植物植物保护的植物。求僵尸能够获得的最大价值。

题目分析

放上来当板子看吧

第一发写的时候zz了,把恒存在的植物环的所有出边给删了……

  1 #include<bits/stdc++.h>
  2 const int maxn = 33;
  3 const int maxNode = 5035;
  4 const int maxm = 1000035;
  5 const int INF = 2e9;
  6 
  7 struct Edge
  8 {
  9     int u,v,f,c;
 10     Edge(int a=0, int b=0, int c=0, int d=0):u(a),v(b),f(c),c(d) {}
 11 }edges[maxm];
 12 int r,c,S,T,ans;
 13 int v[maxn][maxn],id[maxn][maxn];
 14 int edgeTot,head[maxNode],nxt[maxm],lv[maxNode];
 15 int low[maxNode],dfn[maxNode],tim;
 16 int stk[maxNode],top,col[maxNode],cols,size[maxNode];
 17 
 18 int read()
 19 {
 20     char ch = getchar();
 21     int num = 0, fl = 1;
 22     for (; !isdigit(ch); ch=getchar())
 23         if (ch=='-') fl = -1;
 24     for (; isdigit(ch); ch=getchar())
 25         num = (num<<1)+(num<<3)+ch-48;
 26     return num*fl;
 27 }
 28 void addedge(int u, int v, int c)
 29 {
 30     edges[edgeTot] = Edge(u, v, 0, c), nxt[edgeTot] = head[u], head[u] = edgeTot, ++edgeTot;
 31     edges[edgeTot] = Edge(v, u, 0, 0), nxt[edgeTot] = head[v], head[v] = edgeTot, ++edgeTot;
 32 }
 33 bool buildLevel()
 34 {
 35     std::queue<int> q;
 36     memset(lv, 0, sizeof lv);
 37     q.push(S), lv[S] = 1;
 38     for (int tmp; q.size(); )
 39     {
 40         tmp = q.front(), q.pop();
 41         for (int i=head[tmp]; i!=-1; i=nxt[i])
 42         {
 43             int v = edges[i].v;
 44             if (!lv[v]&&edges[i].f < edges[i].c){
 45                 lv[v] = lv[tmp]+1, q.push(v);
 46                 if (v==T) return true;
 47             }
 48         }
 49     }
 50     return false;
 51 }
 52 int fndPath(int x, int lim)
 53 {
 54     int sum = 0;
 55     if (x==T||!lim) return lim;
 56     for (int i=head[x]; i!=-1&&sum < lim; i=nxt[i])
 57     {
 58         int v = edges[i].v, val;
 59         if (lv[v]==lv[x]+1&&edges[i].f < edges[i].c){
 60             if ((val = fndPath(v, std::min(lim-sum, edges[i].c-edges[i].f)))){
 61                 sum += val, edges[i].f += val, edges[i^1].f -= val;
 62             }else lv[v] = -1;
 63         }
 64     }
 65     return sum;
 66 }
 67 int dinic()
 68 {
 69     int ret = 0, val;
 70     while (buildLevel())
 71         while ((val = fndPath(S, INF))) ret += val;
 72     return ret;
 73 }
 74 void tarjan(int x)
 75 {
 76     dfn[x] = low[x] = ++tim, stk[++top] = x;
 77     for (int i=head[x]; i!=-1; i=nxt[i])
 78         if ((i&1)==0){
 79             int v = edges[i].v;
 80             if (!dfn[v]) tarjan(v), low[x] = std::min(low[x], low[v]);
 81             else if (!col[v]) low[x] = std::min(low[x], dfn[v]);
 82         }
 83     if (low[x]==dfn[x]){
 84         col[x] = ++cols, size[cols] = 1;
 85         for (; stk[top]!=x; --top)
 86             col[stk[top]] = cols, ++size[cols];
 87         --top;
 88     }
 89 }
 90 int main()
 91 {
 92     memset(head, -1, sizeof head);
 93     r = read(), c = read(), S = 0, T = r*c+1;
 94     for (int i=1, cnt=0; i<=r; i++)
 95         for (int j=1; j<=c; j++)
 96             id[i][j] = ++cnt;
 97     for (int i=1; i<=r; i++)
 98         for (int j=1; j<=c; j++)
 99         {
100             v[i][j] = read();
101             if (j!=c) addedge(id[i][j], id[i][j+1], INF);
102             for (int k=read(); k; k--)
103                 addedge(id[read()+1][read()+1], id[i][j], INF);
104         }
105     for (int i=1; i<=r*c; i++)
106         if (!dfn[i]) tarjan(i);
107     for (int i=1; i<=r; i++)
108         for (int j=1; j<=c; j++)
109             if (size[col[id[i][j]]]==1){
110                 if (v[i][j] < 0) addedge(id[i][j], T, -v[i][j]);
111                 else addedge(S, id[i][j], v[i][j]), ans += v[i][j];
112             }
113     printf("%d\n",ans-dinic());
114     return 0;
115 }

 

 

END

转载于:https://www.cnblogs.com/antiquality/p/10513273.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值