BZOJ1565 [NOI2009]植物大战僵尸 【最大权闭合子图 + tarjan缩点(或拓扑)】
题目
输入格式
输出格式
仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为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;
}