网络流24题题解
其实只有23题有一题我不会嘤$QAQ$
算了先把其它23题写了复健下,,,感$jio$不写题解的话就学了和没学似的嘤,,,
然后先分别放个模板趴,,,因为并没学会有上下界的网络流所以只有最基础的最大流和费用流$QwQ$
(upd:,,,我今天康了眼发现我似乎$dinic$打错了,,,有时间改下$QwQ$
#include<bits/stdc++.h> using namespace std; #define il inline #define gc getchar() #define ri register int #define rc register char #define rb register bool #define t(i) edge[i].to #define fy(i) edge[i].fy #define w(i) edge[i].wei #define rp(i,x,y) for(ri i=x;i<=y;++i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=1; const int inf=1e9; int ed_cnt=-1,head[N],dep[N],S,T,cost,cur[N]; struct ed{int to,nxt,wei;}edge[N]; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch<'0' || ch>'9'))ch=gc; if(ch=='-')ch=gc,y=0; while('0'<=ch && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z){edge[++ed_cnt]=(ed){x,head[y],z};head[y]=ed_cnt;edge[++ed_cnt]=(ed){y,head[x],0};head[x]=ed_cnt;} il bool bfs() { queue<int>Q;Q.push(S);memset(dep,0,sizeof(dep)); while(!Q.empty()){ri nw=Q.front();Q.pop();e(i,nw)if(w(i) && !dep[t(i)])Q.push(t(i)),dep[t(i)]=dep[nw]+1;} return dep[T]; } il int dfs(ri nw,ri flow) { if(nw==T || !flow)return flow;ri ret=0; for(ri &i=cur[nw];~i;i=edge[i].nxt) if(dep[t(i)]==dep[nw]+1 && w(i)){ri tmp=dfs(t(i),min(flow-ret,w(i)));w(i)-=tmp,w(i^1)+=tmp,ret+=tmp;if(ret==flow)break;} return ret; } il void dinic(){while(bfs()){rp(i,1,N-1)cur[i]=head[i];while(int d=dfs(S,inf))cost+=d;}} int main() { memset(head,-1,sizeof(head)); return 0; }
#include<bits/stdc++.h> using namespace std; #define il inline #define gc getchar() #define ri register int #define rc register char #define rb register bool #define t(i) edge[i].to #define fy(i) edge[i].fy #define w(i) edge[i].wei #define rp(i,x,y) for(ri i=x;i<=y;++i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=1; const int inf=1e9; int dis[N],fr_ed[N],fr_nod[N],S,T,ed_cnt=-1,head[N],cost; bool vis[N]; struct ed{int to,nxt,wei,fy;}edge[N]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch<'0' || ch>'9'))ch=gc; if(ch=='-')ch=gc,y=0; while('0'<=ch && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z,ri p){edge[++ed_cnt]=(ed){x,head[y],z,p};head[y]=ed_cnt;edge[++ed_cnt]=(ed){y,head[x],0,-p};head[x]=ed_cnt;} il bool spfa() { Q.push(S);memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));vis[S]=1;dis[S]=0; while(!Q.empty()) { ri nw=Q.front();Q.pop();vis[nw]=0; e(i,nw)if(w(i) && fy(i)+dis[nw]>dis[t(i)]){dis[t(i)]=fy(i)+dis[nw],fr_ed[t(i)]=i,fr_nod[t(i)]=nw;if(!vis[t(i)])vis[t(i)]=1,Q.push(t(i));} } if(dis[T]==dis[T+1])return 0; ri flow=inf;for(ri i=T;i!=S;i=fr_nod[i])flow=min(flow,w(fr_ed[i]));for(ri i=T;i!=S;i=fr_nod[i])w(fr_ed[i])-=flow,w(fr_ed[i]^1)+=flow;cost+=flow*dis[T]; return 1; } int main() { memset(head,-1,sizeof(head)); return 0; }
最小割问题
因为我最小割问题学得极差,,,所以先搞下最小割的基础知识$QAQ$
概念不说辣,先港下一个重要定理.最大流=最小割
本来不想证的,看到一个很有意思而且挺容易懂的证明方法所以港下$QwQ$
证明分三个部分
1.任意一个流都小于等于任意一个割
考虑用最开始学网络流时候的水管模型.假设现在有个通水管道线,从$A$地运到$B$地,管道有很多分支,这时候有个恐怖分子切了几根水管,把$B$地的供水完全断掉了
这时候考虑,因为有容量限制,所以每一根被砍的管道流出来的水一定小于等于这个管道的容量
然后因为每一根被砍的管道流出来的水本来都应该要流到$B$地的,也就是说流出来的水之和就等于原来的流
然后由割的定义可知每一个管道的容量之和就等于割
所以有任意一个流都小于等于任意一个割
2.可以构造出一个流=一个割
当达到最大流的时候,根据最大流的定义,就已经没有一条能从$S$走到$T$的通路了,否则还可以继续增广
设在此时$S$能到达的点集为$s$,$T$能到达的点集为$t$,显然$s$和$t$满足割的定义,也就是说可以构造出一个$[s,t]$的割,显然此时$st$之间的边就已经满流了嘛$QwQ$
显然的是此时所有满流边的流量和就最大流.
同时把这些满流边都割了就能构造出$[s,t]$的割
于是有此时的流=割
3.最大流=最小割
设2中相等的流为$F_{m}$,割为$C_{m}$,由1得${\forall}F\leq F_{m}=C_{m}\leq C$
所以有最大流=最小割
证毕!
然后关于最小割常见模型和常见建图方法这个,,,$umm$因为做题不够多所以还没有办法总结出什么套路来,慢慢填坑趴$QAQ$
洛谷$P$1251餐巾计划问题
发现每日供应量必须大于等于需求量,相当于必须跑满,即最大流
发现快洗慢洗或者买都要花钱,且要求费用最好,即最小费用流
所以是个最小费用最大流$QwQ$
考虑怎么建图?先把几个限制列出来,相应的找到建边方法,然后就做完了鸭$QwQ$
1.每天洗好的餐巾和购买的新餐巾数之和要满足当天的需求量$r_{i}$
2.买新餐巾,花费$p$元
3.快洗,花费$f$元,$m$天后取到
4.慢洗,花费$s$元,$n$天后取到
5.囤积
昂相应地写下建边方法?
1.按天建边,建两排表示旧毛巾新毛巾,$ST$连向两排,流量为$r_{i}$费用为0
2.$S$向右连流量为$inf$费用为$p$的边
3.从左向右+$m$连流量为$inf$费用为$f$的边
4.从左向右+$n$连流量为$inf$费用为$s$的边
5.前一天左向后一天左连流量为$inf$费用为$s$的边
然后跑个费用流就成,$over$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define int long long #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=2000+5,L=5000000+10,inf=1e9; int n,p,dq,fq,ds,fs,head[N<<1],fr_e[N<<1],fr_nod[N<<1],dis[N<<1],ed_cnt=-1,st,to,cost; bool vis[N<<1]; struct ed{int to,nxt,wei,fy;}edge[L]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z,ri p){edge[++ed_cnt]=(ed){y,head[x],z,p};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0,-p};head[y]=ed_cnt;} il bool spfa() { Q.push(st);memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));vis[st]=1;dis[st]=0; while(!Q.empty()) { ri nw=Q.front();Q.pop();vis[nw]=0; e(i,nw)if(w(i)>0 && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i),fr_e[t(i)]=i,fr_nod[t(i)]=nw;if(!vis[t(i)])vis[t(i)]=1,Q.push(t(i));} } if(dis[to]==dis[to+1])return false; ri flow=1e9; for(ri i=to;i!=st;i=fr_nod[i])flow=min(flow,w(fr_e[i]));for(ri i=to;i!=st;i=fr_nod[i])w(fr_e[i])-=flow,w(fr_e[i]^1)+=flow; cost+=flow*dis[to]; return true; } main() { memset(head,-1,sizeof(head)); n=read();st=0;to=(n<<1)|1; rp(i,1,n){int x=read();ad(st,i,x,0);ad(i+n,to,x,0);} p=read();rp(i,1,n)ad(st,i+n,inf,p); dq=read();fq=read();ds=read();fs=read();rp(i,1,n-dq)ad(i,i+dq+n,inf,fq);rp(i,1,n-ds)ad(i,i+ds+n,inf,fs);rp(i,1,n-1)ad(i,i+1,inf,0); while(spfa());printf("%lld\n",cost); return 0; }
洛谷$P$2762太空飞行计划问题
这题一看就最小割$QwQ$?
考虑建两排点呗,一排是任务一排是机器.$S$向任务连流量为$p$的边,机器向$T$连流量为$c$的边.任务向机器连流量为$inf$的边
然后跑个最小割,$as=\sum p-
\mbox{最小割}
$,理解下就要么不完成任务把左边割了,要么买机器把右边割了,因为强制任务与机器的配对所以中间设为$inf$就不可能被割掉了$QwQ$输出方案不日常瞎搞下就做完了嘛,$over$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define ll long long #define rg register #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define rp(i,x,y) for(rg int i=x;i<=y;++i) #define my(i,x,y) for(rg int i=x;i>=y;--i) #define e(i,x) for(rg int i=head[x];~i;i=edge[i].nxt) const int N=200000+10,M=1000000;const ll inf=1e18; int n,m,ed_cnt=-1,head[N],st,to,dep[N]; ll sum; char tools[10000]; bool vis[N]; struct ed{int to,nxt;ll wei;}edge[N<<1]; il int read() { rg char ch=gc;rg int x=0;rg bool y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il bool bfs() { queue<int>Q;Q.push(st);memset(vis,0,sizeof(vis));memset(dep,0,sizeof(dep));dep[st]=1; while(!Q.empty()){int nw=Q.front();Q.pop();e(i,nw)if(!dep[t(i)] && w(i))dep[t(i)]=dep[nw]+1,Q.push(t(i)),vis[t(i)]=1;} return dep[to]; } int dfs(int x,ll f) { if(x==to || !f)return f;int ret=0,tmp; e(i,x)if(dep[t(i)]==dep[x]+1 && w(i))if(tmp=dfs(t(i),min(f,w(i))))ret+=tmp,w(i)-=tmp,f-=tmp,w(i^1)+=tmp; return ret; } il void ad(int x,int y,ll z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;} int main() { n=read();m=read();memset(head,-1,sizeof(head));st=1;to=n+m+2; rp(i,1,n) { ll x=read();ad(1,i+1,x);ad(i+1,1,0);sum+=x;memset(tools,0,sizeof tools);cin.getline(tools,10000);int ulen=0,tool; while(sscanf(tools+ulen,"%d",&tool)==1) { if(tool==0)ulen++; else{ad(i+1,tool+n+1,inf);ad(tool+n+1,i+1,0);while(tool){tool/=10;++ulen;}} ++ulen; } } rp(i,1,m)ad(i+1+n,n+m+2,read()),ad(n+m+2,i+1+n,0); while(bfs())sum-=dfs(st,inf); rp(i,1,n)if(vis[i+1])printf("%d ",i);printf("\n"); rp(i,1,m)if(vis[i+n+1])printf("%d ",i);printf("\n"); printf("%lld\n",sum); return 0; }
洛谷$P$2766最长不下降子序列问题
$umm$第一小问弱智题不说$QwQ$
第二小问发现限制条件就说每个数最多只能用一次/$kk$.那就相当于每个点的流量都是1呗$QwQ$
考虑拆点
这里顺便拓展下拆点的常见场景?通常在对点进行一定限制(eg:时间($T1$)/次数($T3$)/最小割表示选择($T2$)时考虑拆点$QwQ$($umm$,因为菜菜$gql$见不多识不广所以目前见过的似乎印象中就这几种?如果碰到了新的再补充$QwQ$
欧克然后现在就考虑拆点呗.考虑$T1$用$dp$的时候已经记录了每个点为结尾的最长上升子序列了,所以可以$S$连左侧$f_{i}=1$的点,右侧$f_{i}=mx$连$T$,右侧$f_{i}=f_{j}+1$且$a_{i}\geq a_{j}$的$i$与左侧$j$相连,左右侧$i$相连.然后流量全部设为1,跑个最大流就好$QwQ$
然后第三问其实就和第二问差不多了昂,,,说$a_{1}$和$a_{n}$没限制,就给它们两个把流量改成$inf$就行鸭$QwQ$
然后注意下,因为我这儿第一小问的$dp$的时候是从后往前做的,所以后面就全和我的题解中描述的是反的$QwQ$不过其实差不多,只是注意下就成
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define ll long long #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=20000+10,inf=1e8; int n,x[N],mx,f[N],st,to,ed_cnt=-1,head[N<<1],dep[N<<1],as; struct ed{int to,nxt,wei;}edge[N<<1]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(int x,int y,int z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0};head[y]=ed_cnt;} il bool bfs() { Q.push(st);memset(dep,0,sizeof(dep));dep[st]=1; while(!Q.empty()){int nw=Q.front();Q.pop();e(i,nw)if(!dep[t(i)] && w(i))Q.push(t(i)),dep[t(i)]=dep[nw]+1;} return dep[to]; } il int dfs(int nw,int f) { if(nw==to)return f;int flow,ret=0; e(i,nw)if(w(i) && dep[t(i)]==dep[nw]+1)if(flow=dfs(t(i),min(f,w(i))))f-=flow,ret+=flow,w(i)-=flow,w(i^1)+=flow; return ret; } il void dinic(){while(bfs())as+=dfs(st,inf);} int main() { // freopen("2766.in","r",stdin);freopen("2766.out","w",stdout); memset(head,-1,sizeof(head)); n=read();st=(n<<1)|1;to=st+1; rp(i,1,n)x[i]=read();f[n]=1;my(i,n-1,1){ri ret=0;rp(j,i+1,n)if(x[j]>=x[i])ret=max(ret,f[j]);f[i]=ret+1;mx=max(mx,f[i]);}printf("%d\n",mx); if(mx==1)return printf("%d\n%d\n",n,n),0; rp(i,1,n) { if(f[i]==1)ad(i,to,1);if(f[i]==mx)ad(st,i+n,1); rp(j,i+1,n)if(f[j]==f[i]-1 && x[j]>=x[i])ad(i,j+n,1); ad(i+n,i,1); } dinic();printf("%d\n",as); if(f[1]==mx)ad(st,1+n,inf),ad(1+n,1,inf); ad(n,to,inf);ad(n+n,n,inf); dinic();printf("%d\n",as); return 0; }
洛谷$P$2764最小路径覆盖问题
是一类经典题呢$QwQ$
题目很绕,其实就是说给一个$DAG$,问至少用几条简单路径能将图上所有点全部覆盖?并输出一个合法方案$QwQ$
$umm$考虑如果给每个点都用一条路径覆盖,$as$就是$n$嘛.然后每连一条边就能减少一条路径.
于是考虑跑个最大流呗.现在就只要思考怎么建图了嘛
因为要求简单路径,就说每个点的入度出度都只一条.
欧克,又看到这种对点的次数产生限制的,那就拆点呗,考虑每个点左右分别和$ST$连边流量为1,然后$DAG$上的边就照着那个连就成?流量也是1嘛
然后跑个最大流就做完辣?
输出方案这种我真的懒得写,,,瞎搞就成,想不出来就看下我代码?直接简单粗暴地做就好鸭$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define ll long long #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=5000+20,M=500000+10,inf=1e8; int n,m,ed_cnt=-1,head[N],st,to,dep[N],as; bool vis[N]; struct ed{int to,nxt,wei;}edge[M]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0};head[y]=ed_cnt;} il bool bfs() { Q.push(st);memset(dep,0,sizeof(dep));dep[st]=1; while(!Q.empty()){ri nw=Q.front();Q.pop();e(i,nw)if(!dep[t(i)] && w(i))Q.push(t(i)),dep[t(i)]=dep[nw]+1;} return dep[to]; } il int dfs(ri nw,ri f) { if(nw==to || !f)return f;ri ret=0,flow; e(i,nw)if(w(i) && dep[t(i)]==dep[nw]+1)flow=dfs(t(i),min(f,w(i))),ret+=flow,f-=flow,w(i)-=flow,w(i^1)+=flow; return ret; } il void dinic(){while(bfs())as+=dfs(st,inf);} il int fd(int x){e(i,x)if(t(i)>n && !w(i))return t(i)-n;return x;} il void jud(int x){int nw=x;while(!vis[nw]){printf("%d ",nw);vis[nw]=1;nw=fd(nw);}} int main() { // freopen("2764.in","r",stdin);//freopen("2764.out","w",stdout); memset(head,-1,sizeof(head));n=read();m=read();st=(n<<1)|1;to=st+1; rp(i,1,m){int x=read(),y=read();ad(x,y+n,1);}rp(i,1,n)ad(st,i,1),ad(i+n,to,1); dinic();memset(vis,1,sizeof(vis));rp(i,1,n)vis[i]=0; rp(i,1,n)if(!vis[i])jud(i),printf("\n"); printf("%d\n",n-as); return 0; }
洛谷$P$4012深海机器人问题
因为$PQ$范围是15,所以最多就225个点,可以考虑全设成点鸭$QwQ$
发现这题里面有两个变量,一个是机器人数也就是流量,一个是收益也就是费用.所以跑个最大费用最大流呗
嗷最大费用最大流会跑趴?就直接给所有费用全部取反嘛.
然后建图也不难?首先源点向起点连流量为机器人数收益为0的边.终点向汇点连流量为机器人数收益为0的边.然后关于这个生物标本是一次性的问题,可以考虑连两条边,一条流量为1费用为收益,一条流量为inf费用为0.很好理解不解释了$QwQ$
然后就做完辣?甚至不用拆点$QwQ$
然后点数因为我开始做的时候数组越界了,,,就给瞎开大了点儿,反正$n$的范围贼小开大了也没事儿$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define gc getchar() #define ri register int #define rc register char #define rb register bool #define t(i) edge[i].to #define fy(i) edge[i].fy #define w(i) edge[i].wei #define rp(i,x,y) for(ri i=x;i<=y;++i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=20,M=N*N*10; const int inf=1e9; int dis[M],fr_ed[M],fr_nod[M],S,T,ed_cnt=-1,head[M],cost,nam[N][N],a,b,p,q,nam_cnt; bool vis[M]; struct ed{int to,nxt,wei,fy;}edge[M<<1]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch<'0' || ch>'9'))ch=gc; if(ch=='-')ch=gc,y=0; while('0'<=ch && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z,ri p){edge[++ed_cnt]=(ed){x,head[y],z,p};head[y]=ed_cnt;edge[++ed_cnt]=(ed){y,head[x],0,-p};head[x]=ed_cnt;} il bool spfa() { Q.push(S);memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));dis[S]=0;vis[S]=1; while(!Q.empty()) { int nw=Q.front();Q.pop();vis[nw]=0; e(i,nw)if(w(i) && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i);fr_nod[t(i)]=nw;fr_ed[t(i)]=i;if(!vis[t(i)])Q.push(t(i)),vis[t(i)]=1;} } if(dis[T]==dis[T+1])return 0; ri ret=dis[T+1];for(ri i=T;i!=S;i=fr_nod[i])ret=min(ret,w(fr_ed[i]));for(ri i=T;i!=S;i=fr_nod[i])w(fr_ed[i])-=ret,w(fr_ed[i]^1)+=ret; return cost+=ret*dis[T],1; } /* il bool spfa() { Q.push(S);memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));vis[S]=1;dis[S]=0; while(!Q.empty()) { ri nw=Q.front();Q.pop();vis[nw]=0; e(i,nw)if(w(i) && fy(i)+dis[nw]<dis[t(i)]){dis[t(i)]=fy(i)+dis[nw],fr_ed[t(i)]=i,fr_nod[t(i)]=nw;if(!vis[t(i)])vis[t(i)]=1,Q.push(t(i));} } if(dis[T]==dis[T+1])return 0; ri flow=inf;for(ri i=T;i!=S;i=fr_nod[i])flow=min(flow,w(fr_ed[i]));for(ri i=T;i!=S;i=fr_nod[i])w(fr_ed[i])-=flow,w(fr_ed[i]^1)+=flow;cost+=flow*dis[T]; return 1; } */ int main() { freopen("4012.in","r",stdin);freopen("4012.out","w",stdout); a=read();b=read();p=read();q=read();rp(i,0,p)rp(j,0,q)nam[i][j]=++nam_cnt;S=++nam_cnt;T=++nam_cnt;memset(head,-1,sizeof(head)); rp(i,0,p)rp(j,0,q){if(i!=p)ad(nam[i+1][j],nam[i][j],inf,0);if(j!=q)ad(nam[i][j+1],nam[i][j],inf,0);} rp(i,0,p)rp(j,0,q-1){ri x=read();ad(nam[i][j+1],nam[i][j],1,-x);}rp(j,0,q)rp(i,0,p-1)ad(nam[i+1][j],nam[i][j],1,-read()); rp(i,1,a){ri k=read(),x=read(),y=read();ad(nam[x][y],S,k,0);}rp(i,1,b){ri r=read(),x=read(),y=read();ad(T,nam[x][y],r,0);} while(spfa());printf("%d\n",-cost); return 0; }
洛谷$P$2754家园[CTSC1999]
这题主要难点在于怎么处理时间?怎么判断无解?
先说判断无解这个趴,还是相对而言比较简单的趴$QwQ$考虑怎么样会是无解?因为时间是无限的,所以除非地球和月球不通否则就一定有解.所以在开始判断下地球能否到月球就好$QwQ$
然后关于这个时间,显然是考虑按时间加边.这里可以考虑,二分或者直接枚举.
因为我用的$dinic$,$dinic$就每次在残图上增广嘛,所以直接枚举比较好,二分的话就要每次重新建图每次跑下了嘛$QwQ$
然后考虑怎么建图?显然是要根据时间每次重新建一套地球&月球&太空船&太空站
考虑怎么连边?首先同一个时间内的,太空船向目的地太空站连边,流量为太空船容量.然后不同时间内,太空站的人可以一直滞留着,所以上一轮的太空站向这一轮的太空站连流量为$inf$的边.然后太空船从上一次的目的地过来的,所以上一轮的目的地太空站向这一轮的太空船连流量为太空船容量的边.然后最后给$S$向最初的地球连流量为$K$的边,每个月球向$T$连流量为$inf$的边,然后就欧克辣!哪天$S$和地球的边流量为0就说明转移完了就做完辣!
$over$
然后具体实现的时候我就麻油判断,直接如果枚到了第100天还没有解就输出0了$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define rp(i,x,y) for(rg int i=x;i<=y;++i) #define my(i,x,y) for(rg int i=x;i>=y;--i) #define e(i,x) for(rg int i=head[x];~i;i=edge[i].nxt) const int N=12000+5,inf=1e8; int n,m,ed_cnt=-1,head[N],st,to,hp[N],p[N][50],tim,flow,dep[N],d[N],pos[N],k; struct ed{int to,nxt,wei;}edge[N<<1]; il int read() { rg char ch=gc;rg int x=0;rg bool y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il bool bfs() { queue<int>Q;Q.push(st);memset(dep,0,sizeof(dep));dep[st]=1; while(!Q.empty()){int nw=Q.front();Q.pop();e(i,nw)if(!dep[t(i)] && w(i))dep[t(i)]=dep[nw]+1,Q.push(t(i));} return dep[to]; } int dfs(int x,int f) { if(x==to || !f)return f;int ret=0,tmp; e(i,x)if(dep[t(i)]==dep[x]+1 && w(i))if(tmp=dfs(t(i),min(f,w(i))))ret+=tmp,w(i)-=tmp,f-=tmp,w(i^1)+=tmp; return ret; } il void ad(int x,int y,int z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0};head[y]=ed_cnt;} int main() { // freopen("jy.in","r",stdin);freopen("jy.out","w",stdout); n=read()+2;m=read();k=read();memset(head,-1,sizeof(head));st=0;to=10000; rp(i,1,m){hp[i]=read();d[i]=read();pos[i]=1;rp(j,1,d[i]){p[i][j]=read()+1;if(p[i][j]==0)p[i][j]=n;}} ad(st,1,inf);ad(n,to,inf); rp(tim,1,100) { ad(st,tim*n+1,inf);ad(tim*n+n,to,inf); rp(i,1,m){int tmp1=p[i][pos[i]];pos[i]=pos[i]%d[i]+1;int tmp2=p[i][pos[i]];ad(n*(tim-1)+tmp1,n*tim+tmp2,hp[i]);} rp(i,2,n-1)ad((tim-1)*n+i,tim*n+i,inf); while(bfs())flow+=dfs(st,inf);if(flow>=k)return printf("%d\n",tim),0; } printf("0\n"); return 0; }
洛谷$P$4015运输问题
$umm$懒得分析了,反正一看就最小费用最大流昂.
显然就考虑仓库作一排点,商店作一排点,$S$向每个仓库连流量为$a_{i}$费用为0的边,每个商店向$T$连流量为$b_{i}$费用为0的边,每个仓库向每个商店连流量为$inf$费用为$c_{i,j}$的边,$over$
嗷然后还要输出个最大费用,就重建个边,费用全部改为负数就成,$over$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define ll long long #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=5000+20,M=5e5+10,inf=1e8; int n,m,ed_cnt=-1,head[N],st,to,fr_nod[N],fr_ed[N],dis[N],as,a[N],b[N],c[N][N]; bool vis[N]; struct ed{int to,nxt,wei,fy;}edge[M]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z,ri fy){edge[++ed_cnt]=(ed){y,head[x],z,fy};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0,-fy};head[y]=ed_cnt;} il bool spfa() { Q.push(st);memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));dis[st]=0;vis[st]=1; while(!Q.empty()) { int nw=Q.front();Q.pop();vis[nw]=0; e(i,nw) if(w(i) && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i);fr_nod[t(i)]=nw;fr_ed[t(i)]=i;if(!vis[t(i)])Q.push(t(i)),vis[t(i)]=1;} } if(dis[to]==dis[to+1])return 0; ri ret=dis[to+1];for(ri i=to;i!=st;i=fr_nod[i])ret=min(ret,w(fr_ed[i]));for(ri i=to;i!=st;i=fr_nod[i])w(fr_ed[i])-=ret,w(fr_ed[i]^1)+=ret; return as+=ret*dis[to],1; } int main() { // freopen("4015.in","r",stdin);freopen("4015.out","w",stdout); m=read();n=read();st=n+m+1;to=n+m+2; memset(head,-1,sizeof(head));rp(i,1,m){a[i]=read();ad(st,i,a[i],0);}rp(i,1,n){b[i]=read();ad(i+m,to,b[i],0);}rp(i,1,m)rp(j,1,n){c[i][j]=read();ad(i,j+m,inf,c[i][j]);} while(spfa());printf("%d\n",as);as=0; memset(head,-1,sizeof(head));rp(i,1,m)ad(st,i,a[i],0);rp(i,1,n)ad(i+m,to,b[i],0);rp(i,1,m)rp(j,1,n)ad(i,j+m,inf,-c[i][j]); while(spfa());printf("%d\n",-as); return 0; }
洛谷$P$2756飞行员配对方案问题
$umm$这不弱智题?直接外籍飞行员建一排点,英国飞行员建一排点,然后$S$向外籍飞行员建流量为1的边,英国飞行员向$T$建流量为1的边,能配对的飞行员之间连流量为1的边,跑个最大流,做完了$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define ri rg int #define rc rg char #define rb rg bool #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define rp(i,x,y) for(rg int i=x;i<=y;++i) #define my(i,y,x) for(rg int i=x;i>=y;--i) #define e(i,x) for(rg int i=head[x];i!=-1;i=edge[i].nxt) const int N=100+20,inf=1e8; int n,m,head[N],ed_cnt=-1,st,to,dep[N],flow; bool gdgs=1; struct ed{int to,nxt,wei;}edge[N*20]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;} il void rd(){while(gdgs){ri x=read(),y=read();if(x==-1 && y==-1)return;ad(x,y,1);ad(y,x,0);}} il bool bfs() { Q.push(st);memset(dep,0,sizeof(dep));dep[st]=1; while(!Q.empty()){ri nw=Q.front();Q.pop();e(i,nw){if(!dep[t(i)] && w(i))Q.push(t(i)),dep[t(i)]=dep[nw]+1;}} return dep[to]; } il int dfs(ri nw,ri f) { if(nw==to || !f)return f;ri ret=0,tmp; e(i,nw)if(dep[t(i)]==dep[nw]+1 && w(i))if(tmp=dfs(t(i),min(f,w(i))))ret+=tmp,w(i)-=tmp,w(i^1)+=tmp,f-=tmp; return ret; } il int dinic(){while(bfs())flow+=dfs(st,inf);return flow;} il void output(int x) { e(i,x)if(w(i^1) && t(i)!=st && t(i)!=to)return void(printf("%d %d\n",x,t(i))); } int main() { // freopen("fxypdfawt.in","r",stdin);freopen("fxypdfawt.out","w",stdout); memset(head,-1,sizeof(head));m=read();n=read();rd();st=n+1,to=n+2;rp(i,1,m)ad(st,i,1),ad(i,st,0);rp(i,m+1,n)ad(i,to,1),ad(to,i,0); printf("%d\n",dinic());rp(i,1,m)output(i); return 0; }
洛谷$P$2761软件补丁问题
这道题并不是网络流来着,,,至少我不是用网络流做的$QwQ$
发现错误最多就20个,然后每次修复的时候都要考虑当前有哪些问题是存在的,所以考虑状压呗?
然后考虑下发现应该是跑个最短路就成,所以跑个最短路,然后就做完辣$QwQ$?
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define rp(i,x,y) for(rg int i=x;i<=y;++i) #define my(i,x,y) for(rg int i=x;i>=y;--i) #define e(i,x) for(rg int i=head[x];~i;i=edge[i].nxt) const int N=20+2,M=100+10; int n,m,dis[1<<N],inf,wei[M],b1[M],b2[M],f1[M],f2[M]; bool vis[1<<N]; char s[N]; queue<int>Q; il int read() { rg char ch=gc;rg int x=0;rg bool y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } int main() { n=read();m=read(); rp(i,1,m) { wei[i]=read(); scanf("%s",s);rp(j,0,n-1){if(s[j]=='+')b1[i]|=1<<j;if(s[j]=='-')b2[i]|=1<<j;} scanf("%s",s);rp(j,0,n-1){if(s[j]=='-')f1[i]|=1<<j;if(s[j]=='+')f2[i]|=1<<j;} } memset(dis,63,sizeof(dis));inf=dis[0];dis[(1<<n)-1]=0;Q.push((1<<n)-1);vis[(1<<n)-1]=1; while(!Q.empty()) { int nw=Q.front();Q.pop(); rp(i,1,m) { if(((nw&b1[i])^b1[i]) || (nw&b2[i]))continue; int zt=f2[i]|(nw^(nw&f1[i])); if(dis[nw]+wei[i]<dis[zt]){dis[zt]=dis[nw]+wei[i];if(!vis[zt])Q.push(zt),vis[zt]=1;} } vis[nw]=0; } printf("%d\n",dis[0]==inf?0:dis[0]); return 0; }
洛谷$P$2763试题库问题
题目大意搞懂就不难辣,,,?$QwQ$
直接就开两排点呗,一排题目一排属性.
然后连边也很简单鸭,$S$向每道题连流量为1的边,每道题向对应属性连流量为1的边,各属性向$T$连流量为要求题数的边.
然后看下跑完没就知道是否有解了,输出的话瞎搞一通就成$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=100+5,M=100*20+10,L=M*10+10,inf=1e9; int k,n,head[M],ed_cnt=-1,st,to,dep[M]; struct ed{int to,nxt,wei;}edge[L]; queue<int>Q;vector<int>P[M]; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0};head[y]=ed_cnt;} il bool bfs() { Q.push(st);memset(dep,0,sizeof(dep));dep[st]=1; while(!Q.empty()){int nw=Q.front();Q.pop();e(i,nw)if(w(i) && !dep[t(i)])Q.push(t(i)),dep[t(i)]=dep[nw]+1;} return dep[to]; } il int dfs(int nw,int f) { if(nw==to || !f)return f;int ret=0,flow; e(i,nw)if(dep[t(i)]==dep[nw]+1 && w(i))if(flow=dfs(t(i),min(f,w(i))))ret+=flow,f-=flow,w(i)-=flow,w(i^1)+=flow; return ret; } il void dinic(){while(bfs())dfs(st,inf);} il void jud(int x){e(i,x)if(!w(i) && t(i)>n)return void(P[t(i)-n].push_back(x));} int main() { // freopen("stkwt.in","r",stdin);freopen("stkwt.out","w",stdout); memset(head,-1,sizeof(head));k=read();n=read();st=0;to=n+k+1;rp(i,1,k)ad(n+i,to,read()); rp(i,1,n){ad(st,i,1);int p=read();while(p--)ad(i,read()+n,1);} dinic(); rp(i,1,n)jud(i); rp(i,n+1,n+1+k)e(j,i)if(!(t(j)^to) && w(j))return printf("No Solution!\n"); rp(i,1,k){printf("%d:",i);int sz=P[i].size();rp(j,0,sz-1)printf(" %d",P[i][j]);printf("\n");} return 0; }
洛谷$P$2770航空路线问题
$umm$在非常古早的时候写另一道题的题解的时候就$cue$到了,,,虽然其实只是顺便港了句而已$QwQ$
不过这题和那题其实没啥关系,,,这题还是很正儿八经的网络流的$QwQ$
$umm$看到说要求每个点只能经过一次?一看到这个条件!就应该想到拆点!(详见,$T3$:拆点的自我修养($bushi$
于是考虑给每个城市拆成两个点,意为到达&飞出,中间连一条流量为1的边就能有效约束
但是还有一个问题昂,就,因为这个只能经过一次的条件约束,导致流量被限制了,所以要统计经过了几个城市,显然是跑个费用流$QwQ$
考虑每经过一个城市,就相当于到达了并飞出了,所以同个城市之间的边是流量为1费用为1
然后还有一个限制条件不能忘了鸭/$kk$
就,要先从西到东再从东到西.这个倒是和我说的那个古早题解的想法是一样儿的,可以当作是跑了两次从东到西,所以连边的话就从东向西连嘛,因为边对答案没有贡献,且显然只能经过一次,所以连一条流量为1费用为0的边(其实,连一条流量为$inf$费用为0的边也不是不可,,,就边的流量其实麻油限制也没事儿,反正在点那儿已经限制过了鸭$QwQ$
最后就还有个小细节是因为最东端最西端城市是分别要经过两次的,所以他们俩都是,同割城市之间的边的流量为2昂$QwQ$
这题显然不需要超级源点超级汇点,我就不用$ST$辣$QwQ$
感$jio$表述得不太好,,,算了不明白的看代码?代码也没看懂直接在下面评论趴,,,懒得组织语言了$QwQ$
输出方案我用的$dfs$,$over$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define ll long long #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=5000+20,M=500000+10,inf=1e8; int n,m,dis[N<<1],ed_cnt=-1,head[N<<1],fr_nod[N<<1],fr_ed[N<<1],flow,as_num; bool vis[N<<1],gg[M]; string s[N]; map<string,int>nam; struct ed{int to,nxt,wei,fy;}edge[M]; queue<int>Q; vector<int>as; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z,ri p){edge[++ed_cnt]=(ed){y,head[x],z,p};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0,-p};head[y]=ed_cnt;} il bool spfa() { Q.push(1);memset(dis,0,sizeof(dis));dis[1]=-1;vis[1]=1; while(!Q.empty()) { int nw=Q.front();Q.pop();vis[nw]=0; e(i,nw)if(w(i) && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i);fr_nod[t(i)]=nw;fr_ed[t(i)]=i;if(!vis[t(i)])Q.push(t(i)),vis[t(i)]=1;} } if(!dis[n<<1])return 0; int ret=1e8;for(ri i=(n<<1);i!=1;i=fr_nod[i])ret=min(ret,w(fr_ed[i])); for(ri i=(n<<1);i!=1;i=fr_nod[i])w(fr_ed[i])-=ret,w(fr_ed[i]^1)+=ret; flow+=ret;as_num+=ret*dis[n<<1]; return 1; } void output(int x) { as.push_back(x);if(!(x^n))return;e(i,x+n)if(!vis[i] && w(i^1)){vis[i]=1;output(t(i));return;} } int main() { // freopen("2770.in","r",stdin);freopen("2770.out","w",stdout); n=read();m=read();rp(i,1,n){cin>>s[i];nam[s[i]]=i;}memset(head,-1,sizeof(head)); rp(i,1,m){string str1,str2;cin>>str1>>str2;int fr=nam[str1],to=nam[str2];if(fr>to)swap(fr,to);ad(fr+n,to,inf,0);} ad(1,1+n,2,0);ad(n,n+n,2,0);rp(i,2,n-1)ad(i,i+n,1,-1); while(spfa());if(flow<2)return printf("No Solution!\n"),0; printf("%d\n",-as_num);output(1); int sz=as.size();rp(i,0,sz-1)cout<<s[as[i]]<<endl;as.clear(); output(1);sz=as.size();my(i,max(sz-2,0),0)cout<<s[as[i]]<<endl; return 0; }
洛谷$P$2765魔术球问题
可以先预处理下哪些数的和是完全平方数?预处理个几千的就差不多辣$QwQ$
然后用类似上面家园那题的方法,一个个加入球,判断能否放入,能放就继续放,然后输出方案显然瞎搞一同就成
于是现在问题就变成如何判断能放入几个球了?这就有点儿像最小路径覆盖问题了?
于是就同样是总数-最大流呗,预处理的数相当于就是最小路径覆盖中的一条边,相同的套路做就好$QwQ$
$umm$然后再解释下为什么分开的两个点不相连趴?首先要确定的是如果相连这个最大流就没意义辣,,,这是显然的,不过这不算是讲道理,还是解释下为什么是这么连的$QwQ$?
首先要发现事实上这个决定之间是彼此无瓜的,就比如说对于15,它可以放到1上,也可以放到10上,都是平方数.然后21可以放到15的上面,显然不管15放在哪个的上面,或者不放别人的上面就呆在自己的柱子处,21都可以放在15的上面.所以决定之间是彼此无瓜的
然后现在是问有几个球离开柱子了,所以就直接这么连就好$QwQ$
$umm$感觉还是比较感性的解释呢,,,算了自己意会趴$QAQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define ll long long #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=5000+20,inf=1e8; int n,m,ed_cnt,dep[N],head[N],st,to; bool vis[N],gdgs=1; struct ed{int to,nxt,wei;}edge[200000+10]; queue<int>Q; vector<int>lk[N]; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0};head[y]=ed_cnt;} il bool bfs() { queue<int>Q;Q.push(st);memset(dep,0,sizeof(dep));dep[st]=1; while(!Q.empty()){ri nw=Q.front();Q.pop();e(i,nw)if(!dep[t(i)] && w(i))dep[t(i)]=dep[nw]+1,Q.push(t(i));} return dep[to]; } int dfs(ri x,ri f) { if(x==to || !f)return f;ri ret=0,tmp; e(i,x)if(dep[t(i)]==dep[x]+1 && w(i))tmp=dfs(t(i),min(f,w(i))),ret+=tmp,w(i)-=tmp,f-=tmp,w(i^1)+=tmp; return ret; } il int dinic(){ri as=0;while(bfs())as+=dfs(st,inf);return as;} il bool check(ri x){ri tmp=sqrt(x);return tmp*tmp==x;} il void pre(){rp(i,1,1600)rp(j,i+1,1600)if(check(i+j))lk[i].push_back(j);} il void link(ri x) { memset(head,-1,sizeof(head));ed_cnt=-1; rp(i,1,x-1) { ri sz=lk[i].size(); rp(j,0,sz-1){if(lk[i][j]>x)break;ad(i,lk[i][j]+1600,1);} } rp(i,1,x)ad(st,i,1);rp(i,1,x)ad(i+1600,to,1); } il int jud(){ri tmp=0;while(gdgs){link(++tmp);if(tmp-dinic()>n)return tmp-1;}} il void output(int x){vis[x]=1;printf("%d ",x);e(i,x)if(!w(i) && t(i)!=st && t(i)!=to)return void(output(t(i)-1600));} int main() { // freopen("2765.in","r",stdin);freopen("2765.out","w",stdout); pre();st=3201;to=3202;pre();n=read(); ri m=jud();link(m);dinic();printf("%d\n",m); rp(i,1,m)if(!vis[i])output(i),printf("\n"); return 0; }
洛谷$P$3254圆桌问题
$umm$无脑题,,,?
直接开一排点表示桌子,开一排点表示单位,因为一个桌子上一个单位的最多一人,所以单位和桌子之间就连流量为1的边就成.然后再$S$和桌子连流量为容量的边,单位和$S$连流量为人数的边,做完了$QwQ$
输出方案的话不瞎搞一通就成?
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define ll long long #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=500+20,inf=1e8; int n,m,ed_cnt=-1,dep[N],head[N],st,to,tot,as; struct ed{int to,nxt,wei;}edge[200000+10]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0};head[y]=ed_cnt;} il bool bfs() { queue<int>Q;Q.push(st);memset(dep,0,sizeof(dep));dep[st]=1; while(!Q.empty()){ri nw=Q.front();Q.pop();e(i,nw)if(!dep[t(i)] && w(i))dep[t(i)]=dep[nw]+1,Q.push(t(i));} return dep[to]; } int dfs(int x,int f) { if(x==to || !f)return f;ri ret=0,tmp; e(i,x)if(dep[t(i)]==dep[x]+1 && w(i))tmp=dfs(t(i),min(f,w(i))),ret+=tmp,w(i)-=tmp,f-=tmp,w(i^1)+=tmp; return ret; } il void dinic(){while(bfs())as+=dfs(st,inf);} int main() { // freopen("3254.in","r",stdin);//freopen("3254.out","w",stdout); memset(head,-1,sizeof(head));m=read();n=read();st=n+m+1;to=n+m+2; rp(i,1,m){int x=read();tot+=x;ad(st,i,x);}rp(i,1,n)ad(i+m,to,read());rp(i,1,m)rp(j,1,n)ad(i,j+m,1); dinic();if(as^tot)return printf("0\n"),0;printf("1\n");rp(i,1,m){e(j,i)if(t(j)>m && !w(j))printf("%d ",t(j)-m);printf("\n");} return 0; }
洛谷$P$2774方格取数问题
这不一看就最小割问题?非常套路地搞下就成?
具体实现的话就,先黑白染色,分别开一排点,然后相邻的格子之间连流量为$inf$的边,$S$和左侧格子.右侧格子和$T$分别连流量为格子上的数字的边,跑个最小割,做完了$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=100+5,M=100*20+10,L=M*10+10,inf=1e9; int m,n,nam[N][N],num,head[M],ed_cnt=-1,st,to,as,tot,dep[M],movx[]={0,0,1,-1},movy[]={1,-1,0,0}; struct ed{int to,nxt,wei;}edge[L]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0};head[y]=ed_cnt;} il bool bfs() { Q.push(st);memset(dep,0,sizeof(dep));dep[st]=1; while(!Q.empty()){int nw=Q.front();Q.pop();e(i,nw)if(w(i) && !dep[t(i)])Q.push(t(i)),dep[t(i)]=dep[nw]+1;} return dep[to]; } il int dfs(int nw,int f) { if(nw==to || !f)return f;int ret=0,flow; e(i,nw)if(dep[t(i)]==dep[nw]+1 && w(i))if(flow=dfs(t(i),min(f,w(i))))ret+=flow,f-=flow,w(i)-=flow,w(i^1)+=flow; return ret; } il void dinic(){while(bfs())as+=dfs(st,inf);} int main() { // freopen("fgqswt.in","r",stdin);freopen("fgqswt.out","w",stdout); memset(head,-1,sizeof(head));m=read(),n=read();st=0;to=n*m+1; rp(i,1,m)rp(j,1,n){int tmp=read();nam[i][j]=++num;if((i+j)&1)ad(st,nam[i][j],tmp);else ad(nam[i][j],to,tmp);tot+=tmp;} rp(i,1,m)rp(j,1,n)if((i+j)&1)rp(k,0,3)if(i+movx[k] && i+movx[k]<=m && j+movy[k] && j+movy[k]<=n)ad(nam[i][j],nam[i+movx[k]][j+movy[k]],inf); dinic();printf("%d\n",tot-as); return 0; }
洛谷$P$3355骑士共存问题
$umm$最小割呗?就和上面那题差不多?就连边的格子变了点儿,然后就做完了?$QwQ$
然后染色这个,其实题目已经给了提示了,,,都给了个图了,所以依旧是黑白染色然后差不多地做就成$QwQ$?
嗷对了这题要当前弧优化,$over$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define gc getchar() #define ri register int #define rc register char #define rb register bool #define t(i) edge[i].to #define fy(i) edge[i].fy #define w(i) edge[i].wei #define rp(i,x,y) for(ri i=x;i<=y;++i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=1; const int inf=1e9; int ed_cnt=-1,head[N],dep[N],S,T,cost,cur[N]; struct ed{int to,nxt,wei;}edge[N]; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch<'0' || ch>'9'))ch=gc; if(ch=='-')ch=gc,y=0; while('0'<=ch && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z){edge[++ed_cnt]=(ed){x,head[y],z};head[y]=ed_cnt;edge[++ed_cnt]=(ed){y,head[x],0};head[x]=ed_cnt;} il bool bfs() { queue<int>Q;Q.push(S);memset(dep,0,sizeof(dep)); while(!Q.empty()){ri nw=Q.front();Q.pop();e(i,nw)if(w(i) && !dep[t(i)])Q.push(t(i)),dep[t(i)]=dep[nw]+1;} return dep[T]; } il int dfs(ri nw,ri flow) { if(nw==T || !flow)return flow;ri ret=0; for(ri &i=cur[nw];~i;i=edge[i].nxt) if(dep[t(i)]==dep[nw]+1 && w(i)){ri tmp=dfs(t(i),min(flow-ret,w(i)));w(i)-=tmp,w(i^1)+=tmp,ret+=tmp;if(ret==flow)break;} return ret; } il void dinic(){while(bfs()){rp(i,1,N-1)cur[i]=head[i];while(int d=dfs(S,inf))cost+=d;}} int main() { memset(head,-1,sizeof(head)); return 0; }
洛谷$P$3356火星探险问题
$umm$和深海机器人不差不多嘛,,,只是多了几个障碍连边的时候多判下就成$QwQ$
嗷做了下之后发现还是有点儿区别昂$QwQ$就,因为这里收益是在点上的,所以是对点有要求,所以要拆.上面那题的话收益在边上,所以之需要约束边就好$QwQ$
$over$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define gc getchar() #define ri register int #define rc register char #define rb register bool #define t(i) edge[i].to #define fy(i) edge[i].fy #define w(i) edge[i].wei #define rp(i,x,y) for(ri i=x;i<=y;++i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=40,M=N*N*10; const int inf=1e9; int dis[M],fr_ed[M],fr_nod[M],S,T,ed_cnt=-1,head[M],cost,a,b,p,q,zt[N][N],num; bool vis[M]; struct ed{int to,nxt,wei,fy;}edge[M<<1]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch<'0' || ch>'9'))ch=gc; if(ch=='-')ch=gc,y=0; while('0'<=ch && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z,ri p){edge[++ed_cnt]=(ed){x,head[y],z,p};head[y]=ed_cnt;edge[++ed_cnt]=(ed){y,head[x],0,-p};head[x]=ed_cnt;} il bool spfa() { Q.push(S);memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));dis[S]=0;vis[S]=1; while(!Q.empty()) { int nw=Q.front();Q.pop();vis[nw]=0;//printf("nw=%d\n",nw); e(i,nw)if(w(i) && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i);fr_nod[t(i)]=nw;fr_ed[t(i)]=i;if(!vis[t(i)])Q.push(t(i)),vis[t(i)]=1;} } if(dis[T]==dis[T+1])return 0; ri ret=dis[T+1];for(ri i=T;i!=S;i=fr_nod[i])ret=min(ret,w(fr_ed[i]));for(ri i=T;i!=S;i=fr_nod[i])w(fr_ed[i])-=ret,w(fr_ed[i]^1)+=ret; return cost+=ret*dis[T],1; } il int nam(ri x,ri y){return (x-1)*p+y;} il void print(ri d){ri x=d/p+1,y=d%p;if(!y)y=p,--x;printf("(%d,%d)\n",x,y);} void dfs(ri num,ri x) { //printf("x=%d T=%d\n",x+p*q,T); //printf("%d:",num);print(x); if(x==nam(q,p))return; x+=p*q; e(i,x){if(w(i^1) && t(i)>x-p*q && t(i)!=x-p*q){printf("%d %d\n",num,t(i)==x+1-p*q),--w(i^1),dfs(num,t(i));return;}}//printf("t=%d w=%d\n",t(i),w(i^1));} } int main() { // freopen("3356.in","r",stdin);freopen("3356.out","w",stdout); num=read();p=read();q=read();S=0;T=p*q*2+1;memset(head,-1,sizeof(head)); ad(nam(1,1),S,num,0);ad(T,nam(q,p)+p*q,num,0); rp(i,1,q)rp(j,1,p){zt[i][j]=read();if(zt[i][j]==2)ad(nam(i,j)+p*q,nam(i,j),1,-1);if(zt[i][j]!=1)ad(nam(i,j)+p*q,nam(i,j),inf,0);} rp(i,1,q)rp(j,1,p) { if(zt[i][j]==1)continue; if(i!=q && zt[i+1][j]!=1)ad(nam(i+1,j),nam(i,j)+p*q,inf,0); if(j!=p && zt[i][j+1]!=1)ad(nam(i,j+1),nam(i,j)+p*q,inf,0); } while(spfa()); rp(i,1,num)dfs(i,nam(1,1)); return 0; }
洛谷$P$4011孤岛营救问题
$umm$这道题长得就很不网络流昂,,,至少我并没有想到怎么用网络流做嘤
考虑用$bfs$?钥匙的话可以压起来作为状态
然后就做完了?$QwQ$
注意一个坑点/$kk$,就说一个房间可以有多把钥匙$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define fi first #define il inline #define sc second #define gc getchar() #define P pair<int,int> #define mp make_pair #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) const int N=10+2,M=100+5; int n,m,p,zt[M][M],ky[M],K,s,tim[M][1<<N]; int mov_x[4]={0,0,1,-1},mov_y[4]={1,-1,0,0}; queue<P>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il int ys(ri x,ri y){return (x-1)*m+y;} il P jy(ri d){ri x=d/m+1,y=d%m;if(!y)y=m,--x;return mp(x,y);} int main() { freopen("4011.in","r",stdin);freopen("4011.out","w",stdout); n=read();m=read();p=read();memset(zt,-1,sizeof(zt));//memset(ky,-1,sizeof(ky)); K=read();rp(i,1,K){ri x=read(),y=read(),pos1=ys(x,y),pos2;x=read(),y=read();pos2=ys(x,y);zt[pos1][pos2]=zt[pos2][pos1]=read();} s=read();rp(i,1,s){ri x=read(),y=read(),z=read();if(z)ky[ys(x,y)]|=(1<<z);}Q.push(mp(ys(1,1),ky[ys(1,1)])); while(!Q.empty()) { ri nw_pos=Q.front().fi,nw_zt=Q.front().sc,nw_tim=tim[nw_pos][nw_zt];Q.pop(); P nw_pair=jy(nw_pos);ri nw_x=nw_pair.fi,nw_y=nw_pair.sc;nw_zt|=ky[nw_pos]; if(nw_pos==ys(n,m))return printf("%d\n",tim[nw_pos][nw_zt]),0; rp(i,0,3) { ri to_x=nw_x+mov_x[i],to_y=nw_y+mov_y[i],to_pos=ys(to_x,to_y); if(!to_x || !to_y || to_x>n || to_y>m)continue; if(tim[to_pos][nw_zt])continue; if(zt[nw_pos][to_pos]==0)continue; if(zt[nw_pos][to_pos]>0)if(!(nw_zt&(1<<zt[nw_pos][to_pos])))continue; tim[to_pos][nw_zt]=nw_tim+1;Q.push(mp(to_pos,nw_zt)); } } printf("-1\n"); return 0; }
洛谷$P$4016负载平衡问题
$umm$其实可以非网络流地做.就贪心+断环为链,瞎搞一通就成,不难懒得说了/$kk$
还是港下网络流做法趴$QwQ$
$umm$不难发现这是个费用流?于是考虑$S$向每个点连流量为初始容量费用为0的边,每个点向$T$连流量为平均数费用为0的边,然后相邻点之间连流量为$inf$费用为1的边,跑个最小费用最大流,$over$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define ll long long #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=5000+20,M=5e5+10,inf=1e8; int n,m,ed_cnt=-1,head[N],st,to,fr_nod[N],fr_ed[N],dis[N],as,a[N],tot; bool vis[N]; struct ed{int to,nxt,wei,fy;}edge[M]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z,ri fy){edge[++ed_cnt]=(ed){y,head[x],z,fy};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0,-fy};head[y]=ed_cnt;} il bool spfa() { Q.push(st);memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));dis[st]=0;vis[st]=1; while(!Q.empty()) { int nw=Q.front();Q.pop();vis[nw]=0; e(i,nw) if(w(i) && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i);fr_nod[t(i)]=nw;fr_ed[t(i)]=i;if(!vis[t(i)])Q.push(t(i)),vis[t(i)]=1;} } if(dis[to]==dis[to+1])return 0; ri ret=dis[to+1];for(ri i=to;i!=st;i=fr_nod[i])ret=min(ret,w(fr_ed[i]));for(ri i=to;i!=st;i=fr_nod[i])w(fr_ed[i])-=ret,w(fr_ed[i]^1)+=ret; return as+=ret*dis[to],1; } int main() { // freopen("4016.in","r",stdin);freopen("4016.out","w",stdout); memset(head,-1,sizeof(head));n=read();rp(i,1,n)a[i]=read(),tot+=a[i];tot/=n;st=n+1;to=n+2; rp(i,1,n)ad(st,i,a[i],0);rp(i,1,n)ad(i,to,tot,0);rp(i,1,n-1)ad(i,i+1,inf,1);ad(n,1,inf,1);rp(i,2,n)ad(i,i-1,inf,1);ad(1,n,inf,1); while(spfa());return printf("%d\n",as),0; }
洛谷$P$4014分配问题
$umm$有点板子,,,?
就人建一排点任务建一排点,$S$向人连流量为1费用为0的边,任务向$T$连流量为1费用为0的边,人向任务连流量为1费用为$c_{i,j}$的边,然后分别跑个最大费用最大流和最小费用最大流,$over$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define ll long long #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=5000+20,M=5e5+10,inf=1e8; int n,m,ed_cnt=-1,head[N],st,to,fr_nod[N],fr_ed[N],dis[N],as,a[N][N]; bool vis[N]; struct ed{int to,nxt,wei,fy;}edge[M]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z,ri fy){edge[++ed_cnt]=(ed){y,head[x],z,fy};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0,-fy};head[y]=ed_cnt;} il bool spfa() { Q.push(st);memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));dis[st]=0;vis[st]=1; while(!Q.empty()) { int nw=Q.front();Q.pop();vis[nw]=0; e(i,nw) if(w(i) && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i);fr_nod[t(i)]=nw;fr_ed[t(i)]=i;if(!vis[t(i)])Q.push(t(i)),vis[t(i)]=1;} } if(dis[to]==dis[to+1])return 0; ri ret=dis[to+1];for(ri i=to;i!=st;i=fr_nod[i])ret=min(ret,w(fr_ed[i]));for(ri i=to;i!=st;i=fr_nod[i])w(fr_ed[i])-=ret,w(fr_ed[i]^1)+=ret; return as+=ret*dis[to],1; } int main() { // freopen("4014.in","r",stdin);freopen("4014.out","w",stdout); n=read();st=(n<<1)|1;to=st+1;rp(i,1,n)rp(j,1,n)a[i][j]=read(); memset(head,-1,sizeof(head));rp(i,1,n)ad(st,i,1,0),ad(i+n,to,1,0); rp(i,1,n)rp(j,1,n)ad(i,j+n,1,a[i][j]); while(spfa());printf("%d\n",as);as=0; memset(head,-1,sizeof(head));rp(i,1,n)ad(st,i,1,0),ad(i+n,to,1,0);rp(i,1,n)rp(j,1,n)ad(i,j+n,1,-a[i][j]); while(spfa());printf("%d\n",-as); return 0; }
洛谷$P$4013数字梯形问题
昂感$jio$和前面那个最长不下降公共子序列有点儿像,,,?
三个小问分别港下趴.
首先一个共同点是,因为收益在点上,所以要拆点$QwQ$
第一小问,互不相交,直接所有边的流量为1,$over$
第二小问,仅在数字节点相交,就相同点之间的流量设为$inf$
第三小问,都允许相交,就除了$S$与第一排点的连边外其他流量都设为$inf$
各跑一遍费用流就做完了$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=20+5,M=5000+10,L=500000+10,inf=1e9; int m,n,a[N][N<<1],nam[N][N<<1],num,head[M],fr_e[M],fr_nod[M],dis[M],ed_cnt,st,to,cost,gdgs; bool vis[M]; struct ed{int to,nxt,wei,fy;}edge[L]; queue<int>Q; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z,ri p) { edge[++ed_cnt]=(ed){y,head[x],z,p};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0,-p};head[y]=ed_cnt; } il bool spfa() { Q.push(st);memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));vis[st]=1;dis[st]=0; while(!Q.empty()) { ri nw=Q.front();Q.pop();vis[nw]=0; e(i,nw)if(w(i)>0 && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i),fr_e[t(i)]=i,fr_nod[t(i)]=nw;if(!vis[t(i)])vis[t(i)]=1,Q.push(t(i));} } if(dis[to]==dis[to+1])return false; ri flow=1e9; for(ri i=to;i!=st;i=fr_nod[i])flow=min(flow,w(fr_e[i]));for(ri i=to;i!=st;i=fr_nod[i])w(fr_e[i])-=flow,w(fr_e[i]^1)+=flow; cost-=flow*dis[to]; return true; } il void wk1() { ++gdgs; memset(head,-1,sizeof(head));ed_cnt=-1;cost=0; rp(i,1,m)ad(st,nam[1][i],1,0); rp(i,1,n+m-1)ad(nam[n][i]+num,to,1,0); rp(i,1,n)rp(j,1,m+i-1)ad(nam[i][j],nam[i][j]+num,1,-a[i][j]); rp(i,1,n-1)rp(j,1,m+i-1)ad(nam[i][j]+num,nam[i+1][j],1,0),ad(nam[i][j]+num,nam[i+1][j+1],1,0); while(spfa()); printf("%d\n",cost); } il void wk2() { ++gdgs; memset(head,-1,sizeof(head));ed_cnt=-1;cost=0; rp(i,1,m)ad(st,nam[1][i],1,0); rp(i,1,n+m-1)ad(nam[n][i]+num,to,inf,0); rp(i,1,n)rp(j,1,m+i-1)ad(nam[i][j],nam[i][j]+num,inf,-a[i][j]); rp(i,1,n-1)rp(j,1,m+i-1)ad(nam[i][j]+num,nam[i+1][j],1,0),ad(nam[i][j]+num,nam[i+1][j+1],1,0); while(spfa()); printf("%d\n",cost); } il void wk3() { ++gdgs; memset(head,-1,sizeof(head));ed_cnt=-1;cost=0; rp(i,1,m)ad(st,nam[1][i],1,0); rp(i,1,n+m-1)ad(nam[n][i]+num,to,inf,0); rp(i,1,n)rp(j,1,m+i-1)ad(nam[i][j],nam[i][j]+num,inf,-a[i][j]); rp(i,1,n-1)rp(j,1,m+i-1)ad(nam[i][j]+num,nam[i+1][j],inf,0),ad(nam[i][j]+num,nam[i+1][j+1],inf,0); while(spfa()); printf("%d\n",cost); } int main() { // freopen("sztxwt.in","r",stdin);freopen("sztxwt.out","w",stdout); m=read();n=read();rp(i,1,n)rp(j,1,m+i-1)a[i][j]=read(),nam[i][j]=++num;st=0;to=(num<<1)|1; wk1();wk2();wk3(); return 0; }
洛谷$P$4009汽车加油行驶
这题我也不是用的网络流,,,
考虑跑个最短路,记录两个状态,位置和油量.
然后每次照着题目中给出的各种决策加入模拟($bushi$)就成,$QwQ$
$over$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define fr first #define sc second #define ll long long #define gc getchar() #define P pair<int,int> #define mp make_pair #define t(i) edge[i].to #define w(i) edge[i].wei #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];i;i=edge[i].nxt) const int N=100+10,inf=1e9; int n,K,a,b,c,nam[N][N],nam_cnt,as,ed_cnt,head[N*N],dis[N*N][20]; bool station[N*N],vis[N*N][20]; struct ed{int to,nxt,wei;}edge[N*N*N]; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch<'0' || ch>'9'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void ad(ri x,ri y,ri z){edge[++ed_cnt]=(ed){y,head[x],z};head[x]=ed_cnt;} il void spfa() { memset(dis,63,sizeof(dis));dis[nam[1][1]][K]=0;queue< P >Q;Q.push(mp(nam[1][1],K)); while(!Q.empty()) { int nwpos=Q.front().fr,nwoil=Q.front().sc;Q.pop();vis[nwpos][nwoil]=0; if(nwoil) { e(i,nwpos) { if(!station[t(i)]) { if(dis[t(i)][nwoil-1]>dis[nwpos][nwoil]+w(i)) { dis[t(i)][nwoil-1]=dis[nwpos][nwoil]+w(i); if(!vis[t(i)][nwoil-1])Q.push(mp(t(i),nwoil-1)),vis[t(i)][nwoil-1]=1; } } else if(dis[t(i)][K]>dis[nwpos][nwoil]+w(i)+a) { dis[t(i)][K]=dis[nwpos][nwoil]+w(i)+a; if(!vis[t(i)][K])Q.push(mp(t(i),K)),vis[t(i)][K]=1; } } } if(dis[nwpos][K]>dis[nwpos][nwoil]+a+c) { dis[nwpos][K]=dis[nwpos][nwoil]+a+c; if(!vis[nwpos][K])Q.push(mp(nwpos,K)),vis[nwpos][K]=1; } } } int main() { // freopen("4009.in","r",stdin);freopen("4009.out","w",stdout); n=read();K=read();a=read();b=read();c=read(); rp(i,1,n)rp(j,1,n)nam[i][j]=++nam_cnt,station[nam[i][j]]=read(); rp(i,1,n)rp(j,1,n) {if(i!=n)ad(nam[i][j],nam[i+1][j],0);if(j!=n)ad(nam[i][j],nam[i][j+1],0);if(i!=1)ad(nam[i][j],nam[i-1][j],b);if(j!=1)ad(nam[i][j],nam[i][j-1],b);} spfa(); as=1e9;rp(i,0,K)as=min(as,dis[nam[n][n]][i]);printf("%d\n",as); return 0; }
洛谷$P$3358最长$k$可重区间集
$umm$长得就很费用流鸭$QwQ$因为关于$k$的限制就相当于流量然后那个$\sum z$就相当于费用鸭
于是就考虑费用流呗$QwQ$?
首先看到端点没有范围,于是考虑先离散化下,只是要记录下实际长度因为最终答案要用到$QwQ$
然后考虑怎么连边$QwQ$?
费用很简单,就长度嘛,这个只要想好了怎么建边,给费用赋值还是挺简单的$QwQ$
关键在于怎么通过合理设置流量保证关于那个$k$的限制.
首先要明白一个点,就说如果一条线段上一个非端点的节点被覆盖超过$K$次,一定有一条覆盖了这个点的线段的一个端点一定被覆盖了超过$K$次.十分显然不想证$QwQ$
然后条件就变成了所有端点的经过次数小于等于$k$
于是就,相邻的点之间连流量为$k$费用为0的边,同一条线段的两个端点连流量为1费用为$len$的边
大概解释下?
就首先解释关于线段的趴,显然一条线段只能选一次,所以流量为1,同时选了这条线段会有$len$的贡献,所以费用为$len$
然后这个相邻点就单纯为了限制每个点被经过次数不超过$k$的鸭,,,然后就做完辣$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define lb(x) lower_bound(st+1,st+1+st_cnt,x)-st #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=5000; int n,K,l[N],r[N],st[N],st_cnt,ed_cnt=-1,head[N],bg,to,cost,fr_nod[N],fr_ed[N],dis[N]; struct ed{int to,nxt,wei,fy;}edge[N*N]; struct line{int l,r,wei;}ln[N]; bool vis[N]; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch<'0' || ch>'9'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il void swap(ri &gd,ri &gs){gd^=gs;gs^=gd;gd^=gs;} il int abs(ri &gd,ri &gs){if(gd>gs)swap(gd,gs);return gs-gd;} il void ad(ri x,ri y,ri z,ri p){edge[++ed_cnt]=(ed){y,head[x],z,p};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0,-p};head[y]=ed_cnt;} il bool spfa() { memset(dis,63,sizeof(dis));memset(vis,0,sizeof(vis));queue<int>Q;Q.push(bg);dis[bg]=0;vis[bg]=1; while(!Q.empty()) { ri nw=Q.front();Q.pop();vis[nw]=0; e(i,nw){if(w(i) && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i),fr_nod[t(i)]=nw,fr_ed[t(i)]=i;if(!vis[t(i)])Q.push(t(i)),vis[t(i)]=1;}} } if(dis[to]==dis[to+1])return 0; ri flow=dis[to+1]; for(ri i=to;i!=bg;i=fr_nod[i])flow=min(flow,w(fr_ed[i]));for(ri i=to;i!=bg;i=fr_nod[i])w(fr_ed[i])-=flow,w(fr_ed[i]^1)+=flow;cost-=dis[to]*flow; return 1; } int main() { // freopen("3358.in","r",stdin);freopen("3358.out","w",stdout); memset(head,-1,sizeof(head));n=read();K=read();rp(i,1,n)ln[i].l=st[++st_cnt]=read(),ln[i].r=st[++st_cnt]=read(),ln[i].wei=abs(ln[i].l,ln[i].r); sort(st+1,st+1+st_cnt); st_cnt=unique(st+1,st+st_cnt+1)-st-1; rp(i,1,n)ln[i].l=lb(ln[i].l),ln[i].r=lb(ln[i].r);bg=0;to=st_cnt+1; rp(i,1,to)ad(i-1,i,K,0);rp(i,1,n)ad(ln[i].l,ln[i].r,1,-ln[i].wei);while(spfa());printf("%d\n",cost); return 0; }
洛谷$P$3358最长$k$可重线段集
和区间集差不多鸭,,,$QwQ$
只要注意一个小$tip$呢,就说对于垂直的情况的话,可以考虑先把所有线段横坐标都×2,然后如果两个相等就给右端点+1,否则左端点+1,然后就欧克了$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define gc getchar() #define t(i) edge[i].to #define w(i) edge[i].wei #define fy(i) edge[i].fy #define ri register int #define rb register bool #define rc register char #define lb(x) lower_bound(st+1,st+1+st_cnt,x)-st #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];~i;i=edge[i].nxt) const int N=5000000+10; int n,K,l[N],r[N],st[N],st_cnt,ed_cnt=-1,head[N],bg,to,cost,fr_nod[N],fr_ed[N],dis[N]; struct ed{int to,nxt,wei,fy;}edge[N]; struct line{int l,r,wei;}ln[N]; bool vis[N]; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch<'0' || ch>'9'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il int cal(ri x,ri y,ri z,ri p){return sqrt(1ll*(x-z)*(x-z)+1ll*(y-p)*(y-p));} il void ad(ri x,ri y,ri z,ri p){edge[++ed_cnt]=(ed){y,head[x],z,p};head[x]=ed_cnt;edge[++ed_cnt]=(ed){x,head[y],0,-p};head[y]=ed_cnt;} il bool spfa() { memset(dis,63,sizeof(dis));memset(vis,0,sizeof(vis));queue<int>Q;Q.push(bg);dis[bg]=0;vis[bg]=1; while(!Q.empty()) { ri nw=Q.front();Q.pop();vis[nw]=0; e(i,nw)if(w(i) && dis[t(i)]>dis[nw]+fy(i)){dis[t(i)]=dis[nw]+fy(i),fr_nod[t(i)]=nw,fr_ed[t(i)]=i;if(!vis[t(i)])Q.push(t(i)),vis[t(i)]=1;} } if(dis[to]==dis[to+1])return 0; ri flow=dis[to+1]; for(ri i=to;i!=bg;i=fr_nod[i])flow=min(flow,w(fr_ed[i]));for(ri i=to;i!=bg;i=fr_nod[i])w(fr_ed[i])-=flow,w(fr_ed[i]^1)+=flow; cost-=dis[to]*flow; return 1; } int main() { // freopen("3357.in","r",stdin);freopen("3357.out","w",stdout); memset(head,-1,sizeof(head)); n=read();K=read(); rp(i,1,n) { ri x=read(),y=read(),xx=read(),yy=read();ln[i].wei=cal(x,y,xx,yy); if(x>xx)swap(x,xx); x<<=1;xx<<=1;if(x^xx)++x;else ++xx;st[++st_cnt]=ln[i].l=x;st[++st_cnt]=ln[i].r=xx; } sort(st+1,st+1+st_cnt); st_cnt=unique(st+1,st+st_cnt)-st-1; rp(i,1,n)ln[i].l=lb(ln[i].l),ln[i].r=lb(ln[i].r);bg=0;to=st_cnt+1; rp(i,1,to)ad(i-1,i,K,0);rp(i,1,n)ad(ln[i].l,ln[i].r,1,-ln[i].wei);while(spfa());printf("%d\n",cost); return 0; }
一些杂七杂八的东西($bushi$
关于最小割的总结在最前面
关于拆点的总结在$T3$处
易错点:
1.ed_cnt初始化-1
2.head初始化-1
3.ad函数里面反向边流量是0
4.cur每次要重新赋值为head
(不重要就不要看了趴????????????:
打算总结下的小套路:黑白染色.数据结构优化建图.前后缀优化建图.费用递增.平面转对偶.$hal$定理.回路限制.切糕模型.文理分科模型.最大权闭合子图.序列限制问题.上下界.线性规划