【NOI2009】植物大战僵尸
题面
题解
先扯一些无关的,最近怀旧重新玩了玩植物大战僵尸,然后发现打到夜晚池塘的时候矿工僵尸发现很难处理于是回来刷了刷题...
看到洛谷上正好有道植物大战僵尸就写了(看完发现是道最大权闭合子图模型的水题)。
行步入正题。
先说一说什么是闭合子图(好像看这名字都可以大致猜出来这是什么东西)
若(G'(V',E'))是(G(V,E))的闭合子图。
则(egin{cases} V' in V, E' in E\ forall (u,v) in E, u in V' & & v in V' end{cases})
最大权闭合子图自然就是权值最大的闭合子图。
那么我们设超级源点(st)和超级汇点(ed)
对于所有正权点,连边(st o u)
对于所有负权点,连边(u o ed)
对于不能被割掉的点(有条件依赖的点),连边权值为(inf)
最后正权点之和减去最小割就是答案。
证明也很简单:
不向(st)和(ed)连边的点容量都是(inf),不可能被割掉。
能被割掉的只有联向(st)和(ed)的点。
设与(st)连通的点集为(A),与(ed)连通的点集为(B)
我们最后保留的一定是(A)集。
如果断掉了某一个正权点的边,意味着这个点(in B)同时也(
otin A),顾总权值减去(val[u])。
同理,如果断掉了一个负权点的边,与上面是相反的,即总权值减去(-val[u]) 。
这道题还有另外一个坑点那就是两棵植物互相保护那我们是无法攻击到的。
如何解决这种情况?
我们建反图然后跑拓扑排序。
然后就是道模板题了,建图可以看我代码or 自己尝试。
代码
#include <bits/stdc++.h>
const int maxid = 970;
const int maxn = 25;
const int maxm = 35;
const int inf = 0x3f3f3f3f;
using std::min;
template<class t> inline void read(t& res) {
res = 0; char ch = getchar(); bool neg = 0;
while(!isdigit(ch))
neg |= ch == '-', ch = getchar();
while(isdigit(ch))
res = (res << 1) + (res << 3) + (ch & 15), ch = getchar();
if(neg)
res = -res;
}
inline int id(int a,int b) { return a * 31 + b; }
int n, m, i, j, k, S, st, ed, cnte = 1;
int dep[maxid], sco[maxid];
int in[maxid]; std::vector<int> rev[maxid];
int hd[maxid], cur[maxid], ver[maxid * maxid << 1], nxt[maxid * maxid << 1], wei[maxid * maxid << 1];
bool vis[maxid];
inline void adde(int u,int v,int w) {
ver[++cnte] = v; wei[cnte] = w; nxt[cnte] = hd[u]; hd[u] = cnte;
ver[++cnte] = u; wei[cnte] = 0; nxt[cnte] = hd[v]; hd[v] = cnte;
}
std::queue<int> q;
inline void tpsort() {
for(int i = 1;i <= n;i++) {
for(int j = 1, I;j <= m;j++) {
I = id(i,j);
if(!in[I])
vis[I] = 1, q.push(I);
}
}
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = 0, le = rev[u].size();i < le;i++) {
int v = rev[u][i];
in[v]--;
if(!in[v] && !vis[v])
vis[v] = 1, q.push(v);
}
}
}
inline bool bfs() {
std::queue<int> q;
memset(dep,0,sizeof(dep));
dep[st] = 1; q.push(st);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = hd[u];~i;i = nxt[i]) {
int v = ver[i]; int w = wei[i];
if(!dep[v] && w) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
return dep[ed];
}
int dfs(int u,int flow) {
if(u == ed)
return flow;
int dec = flow;
for(int& i = cur[u];~i && dec;i = nxt[i]) {
int v = ver[i]; int w = wei[i];
if(dep[u] + 1 == dep[v] && w) {
int tmp = dfs(v,min(w,dec));
dec -= tmp;
wei[i] -= tmp;
wei[i ^ 1] += tmp;
}
}
return flow - dec;
}
inline void dinic() {
while(bfs()) {
memcpy(cur,hd,sizeof(hd));
while(int flow = dfs(st,inf))
S -= flow;
}
return;
}
int main() {
memset(hd,-1,sizeof(hd));
read(n); read(m);
for(int i = 1;i <= n;i++) {
for(int j = 1, I;j <= m;j++) {
I = id(i,j); read(sco[I]);
int w; read(w);
for(int k = 1, r, c, J;k <= w;k++) {
read(r); read(c); r++; c++; J = id(r,c);
rev[I].push_back(J);
in[J]++;
}
if(j < m) {
int J = id(i,j + 1);
rev[J].push_back(I);
in[I]++;
}
}
}
tpsort();
st = maxid - 1, ed = maxid - 2;
S = 0;
for(int i = 1;i <= n;i++) {
for(int j = 1, I;j <= m;j++) {
I = id(i,j);
if(!vis[I])
continue;
if(sco[I] >= 0)
S += sco[I], adde(st,I,sco[I]);
else
adde(I,ed,-sco[I]);
for(int k = 0, le = rev[I].size();k < le;k++) {
int v = rev[I][k];
if(vis[v])
adde(v,I,inf);
}
}
}
dinic();
printf("%d
",S);
return 0;
}