网络流24题

来源:互联网 发布:淘宝店招在线设计 编辑:程序博客网 时间:2024/06/14 00:21

深坑施工...(2/24)


「网络流 24 题」搭配飞行员

题目链接:https://loj.ac/problem/6000


额,板题


代码:

#include<bits/stdc++.h>using namespace std;const int MAXN=110;//点数的最大值 const int MAXM=410;//边数的最大值 const int INF=0x3f3f3f3f;namespace ISAP{struct Edge{int to,next,cap,flow;}edge[MAXM];//注意是MAXM int tol;int head[MAXN];int gap[MAXN],dep[MAXN],cur[MAXN];void init(){tol=0;memset(head,-1,sizeof(head));}void addedge(int u,int v,int w,int rw=0){edge[tol].to=v;edge[tol].cap=w;edge[tol].flow=0;edge[tol].next=head[u];head[u]=tol++;edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=0;edge[tol].next=head[v];head[v]=tol++;}int Q[MAXN];void BFS(int start,int end){memset(dep,-1,sizeof(dep));memset(gap,0,sizeof(gap));gap[0]=1;int front=0,rear=0;dep[end]=0;Q[rear++]=end;while(front!=rear){int u=Q[front++];for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(dep[v]!=-1)continue;Q[rear++]=v;dep[v]=dep[u]+1;gap[dep[v]]++;}}}int S[MAXN];int sap(int start,int end,int N)//N总点数{BFS(start,end);memcpy(cur,head,sizeof(head));int top=0;int u=start;int ans=0;while(dep[start]<N){if(u==end){int Min=INF;int inser;for(int i=0;i<top;i++)if(Min>edge[S[i]].cap-edge[S[i]].flow){Min=edge[S[i]].cap-edge[S[i]].flow;inser=i;}for(int i=0;i<top;i++){edge[S[i]].flow+=Min;edge[S[i]^1].flow-=Min;}ans+=Min;top=inser;u=edge[S[top]^1].to;continue;}bool flag=false;int v;for(int i=cur[u];i!=-1;i=edge[i].next){v=edge[i].to;if(edge[i].cap-edge[i].flow&&dep[v]+1==dep[u]){flag=true;cur[u]=i;break;}}if(flag){S[top++]=cur[u];u=v;continue;}int Min=N;for(int i=head[u];i!=-1;i=edge[i].next)if(edge[i].cap-edge[i].flow&&dep[edge[i].to]<Min){Min=dep[edge[i].to];cur[u]=i;}gap[dep[u]]--;if(!gap[dep[u]])return ans;dep[u]=Min+1;gap[dep[u]]++;if(u!=start)u=edge[S[--top]^1].to;}return ans;}}using namespace ISAP;int main(){//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);int n,m,u,v;init();scanf("%d%d",&n,&m);while(~scanf("%d%d",&u,&v)){addedge(u,v,1);}for(int i=1;i<=m;i++){addedge(0,i,1);}for(int i=m+1;i<=n;i++){addedge(i,n+m+1,1);}printf("%d\n",sap(0,n+m+1,n+m+2));return 0;}


「网络流 24 题」太空飞行计划

题目链接:https://loj.ac/problem/6001


最大权闭合子图:

所谓闭合子图就是给定一个有向图,从中选择一些点组成一个点集V。对于V中任意一个点,其后续节点都仍然在V中。


对于一般的图来说:首先建立源点s和汇点t,将源点s与所有权值为正的点相连,容量为权值;将所有权值为负的点与汇点t相连,容量为权值的绝对值;权值为0的点不做处理;同时将原来的边容量设置为无穷大。


最大权闭合子图的权值等于所有正权点之和减去最小割。


1. 最小割一定是简单割

简单割指得是:割(S,T)中每一条割边都与s或者t关联,这样的割叫做简单割。

因为在图中将所有与s相连的点放入割集就可以得到一个割,且这个割不为正无穷。而最小割一定小于等于这个割,所以最小割一定不包含无穷大的边。因此最小割一定一个简单割。

2. 简单割一定和一个闭合子图对应

闭合子图V和源点s构成S集,其余点和汇点t构成T集。

首先证明闭合子图是简单割:若闭合子图对应的割(S,T)不是简单割,则存在一条边(u,v),u∈S,v∈T,且c(u,v)=∞。说明u的后续节点v不在S中,产生矛盾。

接着证明简单割是闭合子图:对于V中任意一个点u,u∈S。u的任意一条出边c(u,v)=∞,不会在简单割的割边集中,因此v不属于T,v∈S。所以V的所有点均在S中,因此S-s是闭合子图。


由上面两个引理可以知道,最小割也对应了一个闭合子图,接下来证明最小割就是最大权的闭合子图。

首先有割的容量C(S,T)=T中所有正权点的权值之和+S中所有负权点的权值绝对值之和

闭合子图的权值W=S中所有正权点的权值之和-S中所有负权点的权值绝对值之和

则有C(S,T)+W=T中所有正权点的权值之和+S中所有正权点的权值之和=所有正权点的权值之和

所以W=所有正权点的权值之和-C(S,T)


代码:

#include<bits/stdc++.h>using namespace std;typedef long long ll;namespace DINIC{const int MAXN=500+10;const int MAXM=4e4+10;const int INF=0x3f3f3f3f;struct Edge{int u,v,cap,nxt;Edge(){}Edge(int _u,int _v,int _cap,int _nxt):u(_u),v(_v),cap(_cap),nxt(_nxt){}}E[MAXM];int head[MAXN],dis[MAXN],vis[MAXN];int tol;void init(){tol=0;memset(head,-1,sizeof(head));}void addedge(int u,int v,int cap){E[tol]=Edge(u,v,cap,head[u]);//正向边head[u]=tol++;E[tol]=Edge(v,u,0,head[v]);//反向边容量为0head[v]=tol++;}bool BFS(int S,int T){queue<int> q;q.push(S);memset(dis,0x3f,sizeof(dis));dis[S]=0;while(!q.empty()){int x=q.front();q.pop();for(int i=head[x];~i;i=E[i].nxt){if(E[i].cap>0&&dis[E[i].v]==INF){dis[E[i].v]=dis[x]+1;if(E[i].v==T)                    return true;q.push(E[i].v);}}}return dis[T]<INF; //返回是否能够到达汇点}int dfs(int x,int maxflow,int T){if(x==T||maxflow<=0)return maxflow;//i=vis[x]当前弧优化int ret=0;for(int &i=vis[x];~i;i=E[i].nxt){if(dis[E[i].v]==dis[x]+1&&E[i].cap>0){int flow=dfs(E[i].v,min(maxflow,E[i].cap),T);if(flow){ret+=flow;            maxflow-=flow;E[i].cap-=flow;//正向边流量降低E[i^1].cap+=flow; //反向边流量增加}if(maxflow==0)break;}}return ret;//找不到增广路退出}ll dinic(int S,int T,int N){ll ans=0;while(BFS(S,T))//建立分层图{int flow;for(int i=0;i<N;i++)//初始化vis{vis[i]=head[i];}while(flow=dfs(S,INF,T))//一次BFS可以进行多次增广ans+=(ll)flow;}return ans;}}using namespace DINIC;int e[MAXN],I[MAXN];char buf[500],*s;inline int getint(){    int res=0;    while((*s>'9'||*s<'0')&&*s!='\n')++s;    while(*s>='0'&&*s<='9'){        res=res*10-48+*(s++);    }return res;}int main(){//freopen("shut1.in","r",stdin);//freopen("out.txt","w",stdout);int n,m,tot=0;init();scanf("%d%d",&m,&n);for(int i=1;i<=m;i++){int id;scanf("%d",&e[i]);tot+=e[i];fgets(s=buf,500,stdin);while(id=getint()){addedge(i,m+id,INF);}}for(int i=1;i<=n;i++){scanf("%d",&I[i]);}for(int i=1;i<=m;i++){addedge(0,i,e[i]);}for(int i=1;i<=n;i++){addedge(m+i,n+m+1,I[i]);}tot-=dinic(0,n+m+1,n+m+2);for(int i=1;i<=m;i++)  {if(dis[i]!=INF)printf("%d ",i);}printf("\n");for(int i=1;i<=n;i++)if(dis[i+m]!=INF)printf("%d ",i);printf("\n"); printf("%d\n",tot);return 0;}


原创粉丝点击