标签:最大数 递增 art 城市 key 不同 sqrt nlog read
网络流的实战应用篇太难做了,因此先完善这一部分
\(BSOJ2542\)——二分图 最优匹配
两国飞行员\(x\)集合\(y\)集合,\(x\)飞行员可以配对特定的\(y\)集合的飞行员(可无),求一对一配对最大数
二分图最大匹配裸题,最大流实现
建图:(设\(i\in x\)而\(i'\in y\))
\((S,i,1)~(i',T,1)\)
对\((i,j')\)可匹配\((i,j',1)\)
略
\(BSOJ2543\)——最小割 最大权闭合图+输出方案
有\(m\)个实验,每个实验只可以进行一次,但会获得相应的奖金,有\(n\)个仪器,每个实验都需要一定的仪器,每个仪器可以运用于多个实验,但需要一定的价值,问奖金与代价的差的最大值是多少?
我们会发现每个实验都需要一定的仪器相当于实验对仪器有选择的强制约束
这会让我们想到闭合图,权值最大就是最大权闭合图
建图:
设\(i\)表示实验,\(i'\)表示仪器
\((S,i,a_i)~(i,j',INF)~(i',T,b_{i'})\)
求最大权闭合图(正点权-最小割)即可
略
\(BSOJ2569\)——DAG 最小覆盖 最大匹配
求出并输出无向图的一个最小点不相交路径覆盖
首先结论:最小点不相交路径覆盖=点数-最小割
涉及点经过次数拆点入点出点
建图:
\((S,i,INF),(i,i',1),(i,j',1),(i',T,INF)\)
然后输出方案就模仿倒着满流边走,增广中间改即可
dfs改成
inline int dfs(re int x,re int maxflow){
re int dlt,y,res=0;
if(x==T)return maxflow;
for(re int&i=cur[x];i;i=e[i].next){
y=e[i].to;
if(dis[x]==dis[y]+1&&e[i].flow){
dlt=dfs(y,min(maxflow,e[i].flow));
e[i].flow-=dlt;e[i^1].flow+=dlt;
res+=dlt;maxflow-=dlt;
if(dlt>0&&x^S&&y^T&&x<=n&&y>n)a[x].to=y-n,a[y-n].from=x;
if(!maxflow||dis[S]==P)return cur[x]=h[x],res;
}
}
if(!(--gap[dis[x]]))dis[S]=P;++gap[++dis[x]];
return cur[x]=h[x],res;
}
Print就
inline void Print(re int x){
if(!x)return ;
if(a[x].from^x)Print(a[x].from);
vis[x]=1;printf("%d ",x);
}
\(BSOJ2545\)——最小割 最小路径覆盖
满足条件的球可以放在一根柱子上,求最多球再加柱子不够
把合法关系网当成一个无向图
则问题转化为上一题\(BSOJ2569\)——DAG 最小覆盖 最大匹配
枚举个数向内加点加边动态求最小割即可
略
\(BSOJ2546\)——二分图多重匹配
\(n\)个公司,\(m\)个桌子有各自最大座位\(w_i\),每个公司的人不能坐一个桌子,求可行否
不完备的二分图多重匹配
建图:
(\(i\)表公司,\(j\)表桌子)
\((S,i,a_i)\)限定来的人数(最大流跑满上界)
\((i,j',1)\)一个桌一个同公司的人
\((i',T,b_i)\)限定坐得人上界
判定满流即可
略
\(BSOJ2547\)——最大流 dp建图
求一个序列
- 最长递增子序列的长度\(maxl\)。
首先\(O(n^2)dp\)解决第一问,记住不要用\(O(nlog_2 n)\)的因为我们需要保存每个位置结束的最长递增子序列
然后是dp建图的经典套路
先拆点\(i\),\(i'\)
对第二问,建图如下:
首先每个点\((i,i',1)\)
当\(dp_i=1\)表示这个点可以作为开始\((S,i,1)\)
当\(dp_i=maxl\)表示这个点可以作为结束\((i',T,1)\)
其余时刻\((i,j)\)可以中转(\(i\)可以跟\(j\)后)当且仅当\(dp_i+1=dp_j且a_i<a_j且i<j\)则\((i',j,1)\)
对第三问,额外考虑\(1,n\)可以作为起/终点的时候边权改为\(INF\)即可
for(i=1;i<=n;++i){
AddEdge(i,i+n,1);
if(dp[i]==1)AddEdge(S,i,1);
if(dp[i]==ansl)AddEdge(i+n,T,1);
}
for(i=2;i<=n;++i)
for(j=1;j<i;++j)if(a[i]>a[j]&&dp[i]==dp[j]+1)AddEdge(j+n,i,1);
printf("%d\n",ISAP());
cnt=1,memset(h,0,P<<2);
for(i=1;i<=n;++i){
AddEdge(i,i+n,1);
if(dp[i]==1)AddEdge(S,i,1);
if(dp[i]==ansl)AddEdge(i+n,T,1);
}
for(i=2;i<=n;++i)
for(j=1;j<i;++j)if(a[i]>a[j]&&dp[i]==dp[j]+1)AddEdge(j+n,i,1);
AddEdge(S,1,INF);AddEdge(1,1+n,INF);AddEdge(n,n<<1,INF);if(dp[n]==ansl)AddEdge(n<<1,T,INF);
printf("%d\n",ISAP());
//by zzw4257 禁止侵权
\(BSOJ2548\)——最大流 dp建图
有\(n\)道试题,每道试题都标明了所属类别.现要从题库中抽取\(m\)道题组成试卷.并要求试卷包含指定类型的试题.问可行性
几乎同第五题
略
\(BSOJ2549\)——不可做题
\(BSOJ2550\)——黑白染色 二分图最大匹配
从方格中取数,使任意\(2\)个数所在方格没有公共边,且取出的数的总和最大
使任意\(2\)个数所在方格没有公共边这句话让我们想起黑白染色,问题转化为黑白集合的最大独立集
最大独立集=点权和-最小独立集(最小割)
略
\(BSOJ2445\)——线性规划,费用流
考虑全意义
建图:
\((S,i',r_i,p)\)表示在这一天买新餐巾
\((i',T,r_i,0)\)表示这一天用了\(r_i\)的餐巾
\((S,i,r_i,0)\)表示这一天有\(r_i\)条脏餐巾
若\(i<n~(i,i+1,INF,0)\)这一天的脏餐巾剩到第二天
若\(i+m<=n~(i,(i+f)',INF,f)\)送去快洗,INF是因为这一天的脏餐巾不止这一天剩下的,还有之前剩下的
若\(i+t<=n~(i,(i+t)',INF,s)\)送去慢洗,INF是因为这一天的脏餐巾不止这一天剩下的,还有之前剩下的
inline void Read(void){
re int i,x;
n=read(),p=read(),m=read(),f=read(),t=read(),s=read(),S=0,T=n<<1|1,P=T+1;
for(i=1;i<=n;++i){
x=read();
AddEdge(S,i,x,0);
AddEdge(S,i+n,x,p);
AddEdge(i+n,T,x,0);
if(i+m<=n)AddEdge(i,i+m+n,INF,f);
if(i+t<=n)AddEdge(i,i+t+n,INF,s);
if(i^n)AddEdge(i,i+1,INF,0);
}
}
\(BSOJ2551\)——最长不相交路径,最小费用流
给定一张航空图,图中顶点代表城市,边代表两城市间的直通航线。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线。
(1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)。
(2)除起点城市外,任何城市只能访问\(1\)次。
从西到东再到西,实际上就是从西向东飞两次。
建图:
先拆点普通点跑一次\((i,i',1,1)~(i\in(1,n))\)
对最东最西城市可以走两次\((1,1',2,1),(n,n',2,1)\)
边\((x,y)\)有\((x',y,1,0)\)
注意特判横跨\(1,n\)的边
inline void Read(void){
re int i,x,y;re string s1,s2;n=read(),m=read();S=1,T=n<<1,P=T+1;
for(i=1;i<=n;++i)cin>>s1,mp[s1]=i;
for(i=1;i<=m;++i){
cin>>s1>>s2;x=mp[s1],y=mp[s2];
if(x>y)swap(x,y);
if(x==1&&y==n)flag=1;
AddEdge(x+n,y,1,0);
}
for(i=2;i<n;++i)AddEdge(i,i+n,1,1);
AddEdge(1,n+1,2,1),AddEdge(n,n<<1,2,1);
}
inline void Solve(void){Dinic();if(flag&&ansf==1)puts("2");else if(ansf==2)printf("%d\n",ansc-2);else puts("No Solution!");}
\(BSOJ2552\)——状压,最短路
有一些补丁在包含\(b1\)不包含\(b2\)时可以使用,可以去掉\(f1\),增加\(f2\)
用病毒集合来做状态,状态之间跑病毒覆盖的最短路
inline void SPFA(void){
re int i,x,y;re std::queue<int> q;
for(i=T;i<=S;++i)dis[i]=INF;
q.push(S);vis[S]=1;dis[S]=0;
while(!q.empty()){
x=q.front(),q.pop(),vis[x]=0;
for(i=1;i<=m;++i)if(Check(x,i)){
y=(x&(~a[i].f1))|a[i].f2;
if(dis[x]+a[i].v<dis[y]){dis[y]=dis[x]+a[i].v;if(vis[y])continue;q.push(y);}
}
}
if(dis[T]>=INF)puts("0");else printf("%d\n",dis[T]);
}
\(BSOJ2553\)——并查集 最大流
\(n\)个飞船,每个飞船有循环行驶路线,有限载人数,求所有人从月到地最短时间
首先用并查集导出连通性判无解
然后枚举答案(时间)
以时间拆点
建图:
\((S,0,k)\)送走地球上\(k\)个人
\((n+1,T,INF)\)
\((i^k,i^{k+1},INF)\)上标表示时间,表示可以待在空间站里
\((i^k,j^{k+1},s_i(载重))\)表示前一天某一空间站的几个人民可以通过一个飞船坐到后一天的另一个空间站
inline void Solve(void){
re int i,j,p1,p2;
if(getf(0)^getf(n+1))return puts("0"),void();
for(ans=1;;++ans){
for(i=0;i<=n;++i)AddEdge((ans-1)*(n+2)+i,ans*(n+2)+i,INF);
AddEdge(ans*(n+2)+n+1,T,INF);
for(i=1;i<=m;++i){
p1=ans%*sta[i];if(!p1)p1=*sta[i];
p2=(p1+1)%*sta[i];if(!p2)p2=*sta[i];
p1=sta[i][p1],p2=sta[i][p2];
AddEdge((ans-1)*(n+2)+p1,ans*(n+2)+p2,p[i]);
}
k-=ISAP();if(k<=0)return printf("%d\n",ans),void();
}
}
\(BSOJ2554\)——状压 分层图最短路
一个\(n*m\)的方格图,有些临近格子之间需要用钥匙(或者不能通行)花费\(1\)个时间通行,格子里有钥匙,(钥匙数\(\le 10)\),求\((1,1)\)到\((n,m)\)最短时间
作为分层图最短路模板题,讲一下方法
首先钥匙数小,我们以手上持有的钥匙为状态,用状态来分层
状态之间跑最短路转移即可
注意,这题显式建分层图比较麻烦所以模拟状压分层
inline int SPFA(void){
re int i,j,x,y,lay;re deque<Node>q;S=1,T=pos(n,m);
for(i=S;i<=T;++i)for(j=1;j<1<<(p+1);++j)dis[j][i]=INF;
dis[1][S]=0,vis[1][S]=1;q.push_front((Node){1,S});
while(!q.empty()){
lay=q.front().layer;x=q.front().pos,q.pop_front(),vis[lay][x]=0;
dis[lay|key[x]][x]=min(dis[lay|key[x]][x],dis[lay][x]);lay|=key[x];
for(i=h[x];i;i=e[i].next)if(lay&e[i].v){
y=e[i].to;
if(dis[lay][y]>dis[lay][x]+1){
dis[lay][y]=dis[lay][x]+1;
if(vis[lay][y])continue;vis[lay][y]=1;
if(q.size())
if(dis[lay][y]<dis[q.front().layer][q.front().pos])q.push_front((Node){lay,y});
else q.push_back((Node){lay,y});
else q.push_back((Node){lay,y});
}
}
}
res=INF;
for(i=1;i<(1<<(p+1));++i)res=min(res,dis[i][T]);
return res^INF?res:-1;
}
\(BSOJ2555\)——分层图最短路
一个\(n*m\)的方格图,到每个格子可以花费\(p\)加满油(上界\(a\)),四联通格子可以在一个油量的消耗下移动,求\((1,1)\)到\((n,m)\)最小花费
用消耗油量分层,不用状压了,可以一次性建出来图
inline int SPFA(void){
re int i,j,x,y;re deque<int> q;
S=1;
for(i=1;i<=n*n*(m+1);++i)dis[i]=INF;dis[S]=0,vis[S]=1;q.push_front(S);
while(!q.empty()){
x=q.front(),q.pop_front(),vis[x]=0;
for(i=h[x];i;i=e[i].next){
y=e[i].to;
if(dis[y]>dis[x]+e[i].v){
dis[y]=dis[x]+e[i].v;
if(vis[y])continue;vis[y]=1;
if(q.size())
if(dis[y]<dis[q.front()])q.push_front(y);
else q.push_back(y);
else q.push_back(y);
}
}
}
res=INF;
for(i=1;i<=m+1;++i)res=min(res,dis[n*n*i]);
return res;
}
inline void Read(void){
re int i,j,k,tx,ty,dir,x;n=read(),m=read(),a=read(),b=read(),c=read();
for(i=1;i<=n;++i)
for(j=1;j<=n;++j){
x=read();
for(k=1;k<=m;++k)AddEdge(i,j,k,i,j,k+1,0);
if(x){
for(k=2;k<=m+1;++k)AddEdge(i,j,k,i,j,1,a);
for(dir=0;dir<4;++dir){
tx=i+dx[dir],ty=j+dy[dir];
if(tx<1||tx>n||ty<1||ty>n)continue;
if(tx>=i&&ty>=j)AddEdge(i,j,1,tx,ty,2,0);else AddEdge(i,j,1,tx,ty,2,b);
}
}
else{
for(k=1;k<=m;++k){
for(dir=0;dir<4;++dir){
tx=i+dx[dir],ty=j+dy[dir];
if(tx<1||tx>n||ty<1||ty>n)continue;
if(tx>=i&&ty>=j)AddEdge(i,j,k,tx,ty,k+1,0);else AddEdge(i,j,k,tx,ty,k+1,b);
}
}
for(k=2;k<=m+1;++k)AddEdge(i,j,k,i,j,1,a+c);
}
}
}
\(BSOJ2556\)——分层图最短路
给出一个\(n\)层分别为\(m,m+1,\cdots,m+n-1\)个数的梯形,分别求
- 从梯形的顶至底的\(m\)条路径互不相交。
首先拆点\(((x,y),(x,y)')\)
\((S,(1,i),1,0)\)从第一行出发
对点
\(((x,y),(x,y)',点可以被走多少次,v_{x,y})\)
对边
\(((x,y)',(x+1,y),边可以被走多少次,0)\)
\(((x,y)',(x+1,y+1),边可以被走多少次,0)\)
\(((n,i)',T,INF,0)\)结束
inline void Build(re int x,re int y){
re int i,j;cnt=1,memset(h,0,P<<2);
for(i=1;i<=m;++i)AddEdge(S,(pos(1,i)<<1)-1,1,0);
for(i=1;i<n;++i){
for(j=1;j<=i+m-1;++j){
AddEdge((pos(i,j)<<1)-1,pos(i,j)<<1,x,v[i][j]);
AddEdge(pos(i,j)<<1,(pos(i+1,j)<<1)-1,y,0);
AddEdge(pos(i,j)<<1,(pos(i+1,j+1)<<1)-1,y,0);
}
}
for(i=1;i<=n+m-1;++i)AddEdge(pos(n,i)<<1,T,INF,0),AddEdge((pos(n,i)<<1)-1,pos(n,i)<<1,x,v[n][i]);
}
inline void Solve(void){
Build(1,1),Dinic(),printf("%d\n",ansc);
Build(INF,1),Dinic(),printf("%d\n",ansc);
Build(INF,INF),Dinic(),printf("%d\n",ansc);
}
\(BSOJ2557\)——费用流
问一个网络流模型点权费用流最小/最大和
拆点后裸的费用流
inline void Build(re char dlt){
re int i,j;
memset(h,0,P<<2);cnt=1;
for(i=1;i<=n;++i)AddEdge(S,i,a[i],0);
for(i=1;i<=m;++i)AddEdge(i+n,T,b[i],0);
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)AddEdge(i,n+j,INF,dlt*w[i][j]);
}
inline void Solve(void){
Build(1);Dinic();printf("%d\n",ansc);
Build(-1);Dinic();printf("%d\n",-ansc);
}
\(BSOJ2558\)——费用流
问一个二分图最佳匹配
二分图最佳匹配转费用流
建图
\((S,i,1,0)\)
\((i,j',1,w_{i,j'})\)
\((i',T,1,0)\)
略
\(BSOJ2559\)——最小代价供求 费用流
\(n\)个仓库环状排列,只能在相邻仓库间运送,求最少运货量使\(n\)个仓库货量相同
首先这就是均分纸牌一个经典的中位数贪心
同时也是费用流模型最小代价供求
首先我们给每个点\(-ave\),问题转化为让每个点为\(0\)的最小流动量
建图:
即多的要流走那么多\((S,i,a_i,0)\)
少的要留来那么多\((i,T,-a_i,0)\)
两两间可以传递\((i,i+1,INF,1)\)
最小费用流即可
for(i=1;i<=n;++i){scanf("%d",a+i);sum+=a[i];}sum/=n;
for(i=1;i<=n;++i){a[i]-=sum;}
for(i=1;i<=n;++i){
if(a[i]>0)AddEdge(s,i,a[i],0);
else if(a[i]<0)AddEdge(i,t,-a[i],0);
}
for(i=1;i<n;++i){AddEdge(i,i+1,INF,1);AddEdge(i+1,i,INF,1);}AddEdge(1,n,INF,1);AddEdge(n,1,INF,1);
\(BSOJ2560\)——最小代价供求 费用流
\((n+1)*(m+1)\)的方格,四联通,每个点有点权,每个机器人有出发位置和目的地,求点权和最大值
其实很裸的最大费用最大流
建图:
对每个机器人\((S,(x_i,y_i),INF,0)~((x_i,y_i),T,INF,0)\)
因为沿途生物标本由最先遇到它的深海机器人完成采集,所以落脚点只有一次成为采集点\(((x1,y1),(x2,y2),1,c_{x1,y1,x2,y2})\),其余\(((x1,y1),(x2,y2),INF,0)\)
跑最大费用最大流即可
inline void Read(void){
re int i,j,x,y,k;
scanf("%d%d%d%d",&A,&B,&n,&m);s=pos(0,0)-1;t=pos(n,m)+1;
for(i=0;i<=n;++i)
for(j=0;j<m;++j){scanf("%d",&k);x=pos(i,j);y=pos(i,j+1);AddEdge(x,y,1,-k);AddEdge(x,y,INF,0);}
for(j=0;j<=m;++j)
for(i=0;i<n;++i){scanf("%d",&k);x=pos(i,j);y=pos(i+1,j);AddEdge(x,y,1,-k);AddEdge(x,y,INF,0);}
for(i=1;i<=A;++i){scanf("%d%d%d",&k,&x,&y);AddEdge(s,pos(x,y),k,0);}
for(i=1;i<=B;++i){scanf("%d%d%d",&k,&x,&y);AddEdge(pos(x,y),t,k,0);}
}
\(BSOJ2561\)——费用流 离散化
给出\(n\)个区间,每个区间权值为区间长度.求一种取区间的方案,使得每个点最多被取\(k\)次,且权值和最大.
建图
对每个点\((p_i,p_{i+1},k,0)\)(离散化后点的位置)
对每个区间\((l_i,r_i,1,len_i)\)
求最大费用最大流即可
inline void Read(void){
re int i,j;n=read(),k=read(),S=0;
for(i=1;i<=n;++i){
a[i]=(Seg){read(),read()};if(a[i].l>a[i].r)swap(a[i].l,a[i].r);
x[++len]=a[i].l,x[++len]=a[i].r;
}
sort(x+1,x+len+1);len=unique(x+1,x+len+1)-x-1;T=len+1,P=T+1;
for(i=1;i<=n;++i)a[i].l=lower_bound(x+1,x+len+1,a[i].l)-x,a[i].r=lower_bound(x+1,x+len+1,a[i].r)-x;
for(i=1;i<len;++i)AddEdge(i,i+1,INF,0);
AddEdge(S,1,k,0),AddEdge(len,T,k,0);
for(i=1;i<=n;++i)AddEdge(a[i].l,a[i].r,1,x[a[i].r]-x[a[i].l]);
}
\(BSOJ2562\)——费用流 离散化 拆点
给出\(n\)个线段,每个区间权值为线段长度.求一种取线段的方案,使得每个点最多被取\(k\)次,且权值和最大.
基础看上一题
我们直接把线段映射到\(x\)轴上,特例是线段垂直于\(x\)轴的
那么我们做一个转化,把一个点拆成两个,有重复落入的时候就放到一二个,没有时就放第二个
类似的
inline void Read(void){
re int i,x1,y1,x2,y2,t;n=read(),k=read(),S=0;
for(i=1;i<=n;++i){
x1=read(),y1=read(),x2=read(),y2=read();if(x1>x2)std::swap(x1,x2);
t=(int)sqrt(1ll*(x1-x2)*(x1-x2)+1ll*(y1-y2)*(y1-y2));
x1*=2;x2*=2;if(x1==x2)x2++;else x1++;
a[i]=(Seg){x1,x2,t};
x[++len]=a[i].l,x[++len]=a[i].r;
}
std::sort(x+1,x+len+1);len=std::unique(x+1,x+len+1)-x-1;T=len+1,P=T+1;
for(i=1;i<=n;++i)a[i].l=std::lower_bound(x+1,x+len+1,a[i].l)-x,a[i].r=std::lower_bound(x+1,x+len+1,a[i].r)-x;
for(i=1;i<len;++i)AddEdge(i,i+1,INF,0);
AddEdge(S,1,k,0),AddEdge(len,T,k,0);
for(i=1;i<=n;++i)AddEdge(a[i].l,a[i].r,1,a[i].len);
}
\(BSOJ2563\)——\(oj\)暂无\(SPJ\)
\(BSOJ2564\)——黑白染色 二分图最大独立集
给出一个\(n*n\)的棋盘和不能下子的位置
首先黑白染色
有限制的点一定为不同颜色集合,互相连边
最大独立集=点权和-最小独立集(最小割)
跑最大流即可
略
标签:最大数 递增 art 城市 key 不同 sqrt nlog read
原文地址:https://www.cnblogs.com/66t6/p/12093188.html