题解 P3410 【拍照】

Rachel_in

2019-05-29 11:12:03

Solution

介绍一下闭合图的概念,闭合图是建立在有向图上的,就是对于一个集合内每个点,它能达到的所有点都在这个集合里面。 对于样例: ``` 输入 2 3 10 1 2 0 25 2 3 0 5 6 7 输出 17 ``` 我们构成的图为: ![](https://cdn.luogu.com.cn/upload/pic/59707.png) 对于最大权值闭合图,我们需要用**最小割**来做。 我们重新构图: ![](https://cdn.luogu.com.cn/upload/pic/59708.png) **前置结论**:最大权值=闭合图的正权值之和-最小割 **理解边的含义**: 我们做的最小割一定不会切到无穷大的边,所以割里的边都是$S->u$或者$u->T$。 **对于割掉一个$S->u$的边含义为在闭合图中不选择$u$这个点,对于割掉一个$u->T$的边含义为在闭合图中选择$u$这个点。** (注意:这里边的含义很难去想它为什么要这么定义的时候,先看下去,之后会明白的) 举个例子:我们在网络流那张图里割去权值为10,5,7这些边,含义为不选择要求1,选择1,3这些点,也就是一共选择要求2,1,3这三个点,这不是一个闭合图,因为2这个点没选。然后我们发现网络流里有非0流的,不是割,**也就是说如果选择的不是网络流的割,那么选到的点集必定不是闭合图**。 证明:如果$S$与$T$连通,则存在点$u$,$v$,使得$S$到$u$有边,$u$到$v$连通,$v$到$T$有边,所以$v$一定是$u$的后继,但选择了$u$,没有选择$v$,不是闭合子图。 **得出结论:只有$S$与$T$不连通时,才能得到闭合子图。** 于是我们要求出的是割,至于要将答案最大化: 最小割=不选的$S$到$u$的边权和+选择的$u$到$S$的边权和 闭合图最大权值=全部正权点之和-不选的正权点之和+选择的负权点之和 加上选择的负权点之和就相当于减去选择的负权点的绝对值之和,也就是选择的$u$到$S$的边权和 所以: **闭合图最大权值=全部正权点之和-不选的$S$到$u$的边权和-选择的$u$到$S$的边权和** 也就是: **闭合图最大权值=全部正权点之和-最小割** 应该可以理解边的含义了吧 [部分参考博客](https://blog.csdn.net/can919/article/details/77603353) **代码**: ```cpp #include<bits/stdc++.h> using namespace std; inline int read(){ int res=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();} while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+ch-'0';ch=getchar();} return res*f; } const int N=255; const int M=40555; const int inf=1e7; int S,T,n,m,fir[N],cur[N],dep[N],nxt[M],go[M],val[M],cnt=1,maxflow,ans; queue<int> Q; inline void Add(int u,int v,int w){ nxt[++cnt]=fir[u]; fir[u]=cnt; go[cnt]=v; val[cnt]=w; } inline bool bfs(){ for(int i=S;i<=T;i++) dep[i]=-1; dep[S]=0; Q.push(S); while(!Q.empty()){ int u=Q.front();Q.pop(); for(int e=fir[u];e;e=nxt[e]){ int v=go[e]; if(dep[v]==-1&&val[e]>0){ dep[v]=dep[u]+1; Q.push(v); } } } if(dep[T]==-1) return 0; else return 1; } int dfs(int u,int flow){ if(u==T) return flow; for(int &e=cur[u];e;e=nxt[e]){ int v=go[e]; if(dep[v]==dep[u]+1&&val[e]>0){ int f=dfs(v,min(flow,val[e])); if(f>0){ val[e]-=f;val[e^1]+=f; return f; } } } return 0; } void Dinic(){ while(bfs()){ for(int i=S;i<=T;i++) cur[i]=fir[i]; while(int d=dfs(S,inf)){ maxflow+=d; } } } int main(){ m=read();n=read(); S=0;T=m+n+1; for(int i=1;i<=m;i++){ int w=read(); ans+=w; Add(S,i,w);Add(i,S,0); int x=read(); while(x){ Add(i,m+x,inf);Add(m+x,i,0); x=read(); } } for(int i=1;i<=n;i++){ int w=read(); Add(m+i,T,w);Add(T,m+i,0); } Dinic(); printf("%d",ans-maxflow); return 0; } ```