当前位置: 代码迷 >> 综合 >> BZOJ1565 [NOI2009]植物大战僵尸 【最大权闭合子图 + tarjan缩点(或拓扑)】
  详细解决方案

BZOJ1565 [NOI2009]植物大战僵尸 【最大权闭合子图 + tarjan缩点(或拓扑)】

热度:85   发布时间:2023-12-25 04:46:03.0

题目

这里写图片描述

输入格式

这里写图片描述

输出格式

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

输入样例

3 2

10 0

20 0

-10 0

-5 1 0 0

100 1 2 1

100 0

输出样例

25

提示

在样例中, 植物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 。

题解

僵尸想吃一个植物,首先得吃掉它前面的植物和保护它的植物
这就是有附属关系的最大权选择问题,即最大权闭合子图

但是要注意图中的环是无敌的,我们要忽略掉这些点
可以用tarjan或拓扑找环
还没完
被环保护的点也是无敌的,所以还要从环出发排除掉被环保护的点【一开始没注意到这个WA了2发】

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
using namespace std;
const int maxn = 1005,maxm = 2000005,INF = 1000000000;
inline int RD(){int out = 0,flag = 1; char c = getchar();while (c < 48 || c > 57) {
   if (c == '-') flag = -1; c = getchar();}while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}return out * flag;
}
int s[maxn],id[22][32],N,M,cnt = 0,S,T,f[maxn];
int h[maxn],ne = 0;
struct EDGE{
   int to,nxt,f;}ed[maxm];
void build(int u,int v,int f){ed[ne] = (EDGE){v,h[u],f}; h[u] = ne++;ed[ne] = (EDGE){u,h[v],0}; h[v] = ne++;
}
int dfn[maxn],low[maxn],Scc[maxn],scci = 0,Siz[maxn],st[maxn],top = 0,Cnt = 0;
void dfs(int u){low[u] = dfn[u] = ++Cnt;st[++top] = u; int to;Redge(u){if (k & 1) continue;if (!dfn[to = ed[k].to]) dfs(to);if (!Scc[to]) low[u] = min(low[u],low[to]);}if (dfn[u] == low[u]){scci++;do {Scc[st[top]] = scci; Siz[scci]++;}while (st[top--] != u);}
}
void tarjan(){REP(i,cnt) if (!dfn[i]) dfs(i);
}
int d[maxn],vis[maxn],cur[maxn];
bool bfs(){queue<int> q; int u,to;for (int i = S; i <= T; i++) d[i] = INF,vis[i] = false;q.push(S); vis[S] = true; d[S] = 0;while (!q.empty()){u = q.front(); q.pop();Redge(u) if (ed[k].f && !f[to = ed[k].to] && !vis[to]){d[to] = d[u] + 1; vis[to] = true; q.push(to);}}return vis[T];
}
int dfs(int u,int minf){if (u == T || !minf) return minf;int flow = 0,f,to;if (cur[u] == -2) cur[u] = h[u];for (int& k = cur[u]; k != -1; k = ed[k].nxt)if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(ed[k].f,minf)))){ed[k].f -= f; ed[k ^ 1].f += f;flow += f; minf -= f;if (!minf) break;}return flow;
}
int maxflow(){int flow = 0;while (bfs()){fill(cur,cur + maxn,-2);flow += dfs(S,INF);}return flow;
}
void dfs1(int u){f[u] = true;Redge(u) if ((k & 1) && !f[ed[k].to]) dfs1(ed[k].to);
}
int main(){memset(h,-1,sizeof(h));N = RD(); M = RD(); int t,x,y,tot = 0;REP(i,N) REP(j,M) id[i - 1][j - 1] = ++cnt;for (int i = 0; i < N; i++)for (int j = 0; j < M; j++)if (j + 1 < M)build(id[i][j],id[i][j + 1],INF);S = 0; T = cnt + 1;for (int i = 1; i <= cnt; i++){s[i] = RD(); t = RD();while (t--) x = RD(),y = RD(),build(id[x][y],i,INF);}tarjan();REP(i,cnt) if (Siz[Scc[i]] > 1) dfs1(i);REP(i,cnt) if (!f[i]){if (s[i] >= 0) build(S,i,s[i]),tot += s[i];else build(i,T,-s[i]);}printf("%d",tot - maxflow());return 0;
}