https://www.luogu.org/problemnew/show/P2805

最大权闭合子图的特点是,假如你要选一个结点,则要先选中它的所有子节点。正权连S负权连T,容量为绝对值,原图有向边连容量INF。

这里的特点是在于假如这些结点形成了回路,那么不能选中其中任何一个因为没有突破口。

至于为什么要反向建图,是为了使用拓扑排序把回路以及进入回路的结点剪掉,但是不影响网络流图中从属于回路的子节点。

(反向建图后,原本从属于回路的变成可以进入回路,拓扑排序则会把入度非0的剪掉则一举剪除所有不可取的结点)

#include<bits/stdc++.h>
using namespace std;
#define ll long long /* dinic begin */ const int MAXN=;
const int MAXM=;
//注意网络流要预留反向边
const int INF=0x3f3f3f3f;
struct Edge{
int to,next,cap,flow;
}edge[MAXM]; int tol;
int head[MAXN]; void init(){
tol=;
memset(head,-,sizeof(head));
} void addedge(int u,int v,int w,int rw=){
edge[tol].to=v;edge[tol].cap=w;edge[tol].flow=;
edge[tol].next=head[u];head[u]=tol++;
edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=;
edge[tol].next=head[v];head[v]=tol++;
} vector<Edge> tmpEdge[];
vector<int> G[]; void addedge2(int u,int v,int w,int rw=){
Edge e;
e.to=v;
e.cap=w;
e.flow=;
tmpEdge[u].push_back(e);
} void copyedge(Edge e,int u){
int v=e.to;
int w=e.cap;
addedge(u,v,w);
} int Q[MAXN];
int dep[MAXN],cur[MAXN],sta[MAXN];
bool bfs(int s,int t,int n){
int front=,tail=;
memset(dep,-,sizeof(dep[])*(n+));
dep[s]=;
Q[tail++]=s;
while(front<tail){
int u=Q[front++];
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&dep[v]==-){
dep[v]=dep[u]+;
if(v==t)
return true;
Q[tail++]=v;
}
}
}
return false;
} int dinic(int s,int t,int n=-){
int maxflow=;
if(n==-)
n=t;
n++;//假如把t作为编号最后的点的话传入t就可以了
while(bfs(s,t,n)){
for(int i=;i<n;i++)cur[i]=head[i];
int u=s,tail=;
while(cur[s]!=-){
if(u==t){
int tp=INF;
for(int i=tail-;i>=;i--){
tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow); }
maxflow+=tp;
for(int i=tail-;i>=;i--){
edge[sta[i]].flow+=tp;
edge[sta[i]^].flow-=tp;
if(edge[sta[i]].cap-edge[sta[i]].flow==)
tail=i;
}
u=edge[sta[tail]^].to; }
else if(cur[u]!=-&&edge[cur[u]].cap>edge[cur[u]].flow
&&dep[u]+==dep[edge[cur[u]].to]){
sta[tail++]=cur[u];
u=edge[cur[u]].to;
}
else{
while(u!=s&&cur[u]==-){
u=edge[sta[--tail]^].to;
}
cur[u]=edge[cur[u]].next;
}
}
}
return maxflow;
} /* dinic end */ int n,m;
inline int id(int i,int j){
return (i-)*m+j;
} int indeg[];
bool vis[]; void toposort(){
queue<int>q;
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
int u=id(i,j);
if(indeg[u]==){
vis[u]=true;
q.push(u);
}
}
} while(!q.empty()){
int u=q.front();
q.pop();
for(auto v:G[u]){
indeg[v]--;
if(vis[v]==false&&indeg[v]==){
vis[v]=true;
q.push(v);
}
}
} //vis[i]==true的点是在拓扑排序中出现过的点
//若有其他需要可以改成int vist?
} int sc[]; int main(){
init();
scanf("%d%d",&n,&m);
int s=,t=n*m+; for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
int w;
int u=id(i,j);
scanf("%d%d",&sc[u],&w);
while(w--){
int ii,jj;
scanf("%d%d",&ii,&jj);
ii++,jj++;
int v=id(ii,jj);
addedge2(v,u,INF);
indeg[v]++;
G[u].push_back(v);
}
if(j+<=m){
int v=id(i,j+);
addedge2(u,v,INF);
indeg[u]++;
G[v].push_back(u);
//要反向建图,这样进入环的链其实是可以在网络流建最大权闭合子图的
//最大权闭合子图的关系是一个点的从属结点都要选上自己才能选中
//反向建图后,链上的点就是闭合子图的从属节点,可以只选中链上的点而不选择他们要进入的环
}
}
} toposort();
ll sum=;
int cnt=;
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
int u=id(i,j);
if(vis[u]==false)
continue; if(sc[u]>=){
sum+=sc[u];
addedge(s,u,sc[u]);
cnt++;
}
else{
addedge(u,t,-sc[u]);
}
for(auto k:tmpEdge[u]){
if(vis[k.to])
copyedge(k,u);
}
}
} //printf("cnt=%d sum=%lld\n",cnt,sum);
printf("%lld\n",sum-(ll)dinic(s,t));
}

洛谷 - P2805 - 植物大战僵尸 - 最大流 - 最大权闭合子图的更多相关文章

  1. 【洛谷P3410】拍照题解(最大权闭合子图总结)

    题目描述 小B有n个下属,现小B要带着一些下属让别人拍照. 有m个人,每个人都愿意付给小B一定钱让n个人中的一些人进行合影.如果这一些人没带齐那么就不能拍照,小B也不会得到钱. 注意:带下属不是白带的 ...

  2. 洛谷 P4174 [NOI2006]最大获利 && 洛谷 P2762 太空飞行计划问题 (最大权闭合子图 && 最小割输出任意一组方案)

    https://www.luogu.org/problemnew/show/P4174 最大权闭合子图的模板 每个通讯站建一个点,点权为-Pi:每个用户建一个点,点权为Ci,分别向Ai和Bi对应的点连 ...

  3. BZOJ 1565 植物大战僵尸(拓扑排序+最大权闭合子图)

    图中的保护关系就类似于最大权闭合子图.即你想杀x,你就一定要杀掉保护x的点,那么把x向保护它的点连边.那么题目就转化成了最大权闭合子图的问题. 但是这个图有点特殊啊... 考虑有环的情况,显然这个环以 ...

  4. bzoj 1565 [NOI2009]植物大战僵尸【tarjan+最大权闭合子图】

    一上来以为是裸的最大权闭合子图,上来就dinic -然后没过样例.不得不说样例还是非常良心的给了一个强连通分量,要不然就WA的生活不能自理了 然后注意到有一种特殊情况:每个植物向他保护的植物连边(包括 ...

  5. 【bzoj1565】[NOI2009]植物大战僵尸 拓扑排序+最大权闭合图

    原文地址:http://www.cnblogs.com/GXZlegend/p/6808268.html 题目描述 输入 输出 仅包含一个整数,表示可以获得的最大能源收入.注意,你也可以选择不进行任何 ...

  6. 洛谷P2762 太空飞行计划问题(最大权闭合图)

    题意 有$m$个实验,$n$中器材,每个实验需要使用一些器材 每个实验有收入,每个器材有花费 最大化收入 - 花费 Sol 最大权闭合图的经典应用 从$S$向每个实验连流量为该实验收入的边 从每个器材 ...

  7. 【wikioi】1907 方格取数3(最大流+最大权闭合子图)

    http://www.wikioi.com/problem/1907/ 这题我一开始想到的是状压,看到n<=30果断放弃. 然后也想到了黑白染色,然后脑残了,没想到怎么连边. 很简单的一题 黑白 ...

  8. 洛谷 P2805 [NOI2009]植物大战僵尸 解题报告

    P2805 [NOI2009] 植物大战僵尸 题目描述 Plants vs. Zombies(PVZ)是最近十分风靡的一款小游戏.Plants(植物)和Zombies(僵尸)是游戏的主角,其中Plan ...

  9. P2805 [NOI2009]植物大战僵尸 + 最大权闭合子图 X 拓扑排序

    传送门:https://www.luogu.org/problemnew/show/P2805 题意 有一个n * m的地图,你可以操纵僵尸从地图的右边向左边走,走的一些地方是有能量值的,有些地方会被 ...

随机推荐

  1. Solidworks如何创建投影曲线

    画好草图之后(草图是在上视基准面上画的)然后点击曲线,投影曲线   面选择要投影的曲面,然后就得到了平面曲线在曲面上的投影得到的空间曲线   注意这种方法对于开环轮廓也是可以用的,比如下面,我定义一个 ...

  2. <bgsound> - 背景音乐

    摘要 项目 说明 形式 <bgsound src="..."> 支持 e2+ 标签省略 开始标签:必须,结束标签:无 ■ 说明 bgsound 是 background ...

  3. 【转载】回调函数(callback)是什么?

    一个很形象的例子: 你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货.在这个例子里,你的电话号码就叫回 ...

  4. VB.net版机房收费系统——结账功能实现(调错与优化)

    调错部分 上一篇博客<VB.net版机房收费系统--结账功能实现(代码部分>说的是结账功能的实现,亮出了代码.是在为这篇博客做铺垫.尽管结账功能代码是借鉴的巨人的博客.可是自己比着葫芦画瓢 ...

  5. effctive C++ 读书笔记 条款 16

    条款16 成对使用new和delete时要採取同样形式 #include <iostream> #include <string> using namespace std; / ...

  6. WPF代码注意事项,开发常见问题,知识总结

    代码注意事项: 1.代码实现的样式赋值 XXX.Style = TryFindResource("StyleName") as Style; 2.WPF中FindName方法的使用 ...

  7. SpringMVC实战(注解)

    1.前言 前面几篇介绍了SpringMVC中的控制器以及视图之间的映射方式,这篇来解说一下SpringMVC中的注解,通过注解能够非常方便的訪问到控制器中的某个方法. 2.配置文件配置 2.1 注解驱 ...

  8. JSP复习笔记

    1.注释 <!--这个注释会显示在HTML源码中--> <%--隐藏注释,不会显示在HTML源码中--%> 2.声明 <%! java声明 声明变量,方法等 %> ...

  9. 【bzoj4653】[Noi2016]区间

    离散化+线段树 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstri ...

  10. KbmMW资源汇总(特别是xalion的文章)

    KbmMW框架是收费的,不在此提供下载,如需购买,请自行联系作者Kim Madsen. 网址资源: 官网主页:http://www.components4programmers.com/product ...