//看心情填坑...
飞行员配对方案问题 24题
裸二分图匹配,跑一边Dinic即可,原理类似网络流的Hopcroft-Karp算法在二分图的时间复杂度可以到达$O( sqrt(n)* m )$
实际上Dinic的原理和它类似,实现中也可以跑的很快
#include <bits/stdc++.h> #define ll long long #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define pp pair<int,int> #define rep(ii,a,b) for(auto ii=a;ii<=b;ii++) #define per(ii,a,b) for(auto ii=a;ii>=b;ii--) #define show(x) cout<<#x<<"="<<x<<endl #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl using namespace std; const int maxn=1e5+10; const int maxm=1e6+10; const int INF=0x3f3f3f3f; int casn,n,m,k; int to[maxm],cap[maxm],nex[maxm]; int head[maxn],S,T,nume,ans,dis[maxn]; int q[maxn]; void add(int a,int b,int c){ to[nume]=b,cap[nume]=c,nex[nume]=head[a]; head[a]=nume++; to[nume]=a,cap[nume]=0,nex[nume]=head[b]; head[b]=nume++; } bool bfs(){ memset(dis,-1,sizeof dis); int top=0,end=0; q[end++]=S; dis[S]=0; while(top!=end){ int now=q[top++];top%=maxn; for(int i=head[now];~i;i=nex[i]) if(dis[to[i]]==-1&&cap[i]){ dis[to[i]]=dis[now]+1; q[end++]=to[i];end%=maxn; } } return dis[T]!=-1; } int dfs(int now,int last){ if(now==T) return last; int used=0,flow; for(int i=head[now];~i;i=nex[i]){ if(cap[i]&&dis[to[i]]==dis[now]+1){ flow=last-used; flow=dfs(to[i],min(flow,cap[i])); used+=flow; cap[i]-=flow; cap[i^1]+=flow; if(used==last) return last; } } if(used==0) dis[now]=-1; return used; } void dinic(){ ans=0; while(bfs()) ans+=dfs(S,0x3f3f3f3f); } int main(){ //#define test #ifdef test freopen("in.txt","r",stdin);freopen("out.txt","w",stdout); #endif cin>>m>>n; S=0;T=n+1; memset(head,-1,sizeof head); int a,b; while(cin>>a>>b,~a&&~b) add(a,b,1); for(int i=1;i<=m;i++) add(S,i,1); for(int i=m+1;i<=n;i++) add(i,T,1); dinic(); if(ans) { cout<<ans<<endl; for(int i=0;i<nume;i+=2) if(to[i]!=S&&to[i^1]!=S&&to[i]!=T&&to[i^1]!=T&&cap[i^1]!=0){ printf("%d %d ",to[i^1],to[i]); } } else cout<<"No Solution!"<<endl; #ifdef test fclose(stdin);fclose(stdout);system("out.txt"); #endif return 0; }
负载平衡问题 24题
G 公司有 n个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 n个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。
求最小搬运量
最小费用最大流,首先按照题意所说,建立一个环形的路线,费用为1,容量$INF$
其次,对于大于平均值的,需要向其他仓库输出,换句话说都是些小源点,那就从源点向他链接一条边,容量为多余量,费用为0,
然后,相对应的,对于小于平均值的,需要接受,换句话说都是小汇点,链接到汇点的边,容量为需要的量,费用也是0,
最后跑一边最小费用最大流,由于建图方式,超级源点流出的肯定等于超级汇点收到的,
且流出的仓库肯定不会多输出,流入的仓库也不会多接受,符合要求.
#include <bits/stdc++.h> #define ll long long #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define pp pair<int,int> #define rep(ii,a,b) for(auto ii=a;ii<=b;ii++) #define per(ii,a,b) for(auto ii=a;ii>=b;ii--) #define show(x) cout<<#x<<"="<<x<<endl #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl using namespace std; const int maxn=10000+10; const int maxm=10000+10; const int INF=0x3f3f3f3f; int casn,n,m,k; struct node { int pre,to,cap,cost,next; }e[maxm]; int head[maxn],nume,inq[maxn],sum,ans; int que[maxn],pre[maxn],dis[maxn]; int num[maxn],S,T; inline void addedge(int a,int b,int c,int d){ e[++nume]={a,b,c,d,head[a]}; head[a]=nume; } inline void add(int a,int b,int c,int d){ addedge(a,b,c,d);addedge(b,a,0,-d); } bool spfa(){ for(int i=0;i<=T;i++)dis[i]=INF; dis[S]=que[0]=S; int top=0,end=1; while(top!=end){ int now=que[top++];top%=maxn; for(int i=head[now];i;i=e[i].next){ if(e[i].cap&&dis[e[i].to]>dis[now]+e[i].cost){ pre[e[i].to]=i; dis[e[i].to]=dis[now]+e[i].cost; if(!inq[e[i].to]){ inq[e[i].to]=true; que[end++]=e[i].to;end%=maxn; } } } inq[now]=false; } return dis[T]!=INF; } void dfs(){ int flow=INF; for(int i=pre[T];i;i=pre[e[i].pre]) flow=min(flow,e[i].cap); for(int i=pre[T];i;i=pre[e[i].pre]) { e[i].cap-=flow; e[i^1].cap+=flow; ans+=e[i].cost*flow; } } void mcf(){ ans=0; while(spfa()) dfs(); } int main(){ //#define test #ifdef test freopen("in.txt","r",stdin);freopen("out.txt","w",stdout); #endif IO; cin>>n; nume=1; S=0,T=3*n; for(int i=1;i<=n;i++) cin>>num[i],sum+=num[i]; sum/=n; for(int i=1;i<=n;i++) num[i]-=sum; for(int i=1;i<=n;i++){ if(num[i]>0) add(S,i,num[i],0); else add(i+n,T,-num[i],0); } for(int i=1;i<=n;i++){ if(i!=1){ add(i,i-1,INF,1); add(i,i-1+n,INF,1); } if(i!=n){ add(i,i+1,INF,1); add(i,i+1+n,INF,1); } } add(n,1,INF,1); add(n,1+n,INF,1); add(1,n,INF,1); add(1,n<<1,INF,1); mcf(); cout<<ans<<endl; #ifdef test fclose(stdin);fclose(stdout);system("out.txt"); #endif return 0; }
两个人在一个棋盘上互相交换纸条,从左上到右下,第一个人的纸条只能向右或向下,反之亦然,
每一个点只能经过一次,并且有一个权值$G(i,j)$,要求两个纸条最终经过的所有点权值和最大
最大费用最大流....但是这个题标解应该是DP吧
两个人互相传,实际上等于一个人同时传了2个到下面,于是简化问题,
一开始朴素的建了图,结果错了2个CASE,
方式就是超级源向$(1,1)$连接一条费用0,容量2的边,超级汇点一个道理,然后就用容量1.费用为$G(i,j)$来模拟..最后发现会出现可能会出现一个点通过大小为2的流这种情况,题目实际上是限制每个点的最大流量...
所以..拆点大法好,每个点拆为2个点,一个入,一个出,从入到出连接2个边,一个费用为$G(i,j)$容量为1,一个费用为0,容量为1,这样即使有一个点通过大小为2的流量,也不会导致流量重复计算了
#include <bits/stdc++.h> #define ll long long #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define rep(ii,a,b) for(auto ii=a;ii<=b;ii++) using namespace std; const int maxn=1e5+10; const int maxm=1e6+10; const int INF=0x3f3f3f3f; int casn,n,m,k; int g[123][123]; struct node { int pre,to,cap,cost,next; }e[maxm]; int head[maxn],nume,inq[maxn],sum; int que[maxn],pre[maxn],dis[maxn]; int S,T,ans; inline void addx(int a,int b,int c,int d){ e[++nume]={a,b,c,d,head[a]}; head[a]=nume; } inline void add(int a,int b,int c,int d){ addx(a,b,c,d);addx(b,a,0,-d); } bool spfa(){ rep(i,0,T) dis[i]=-INF; int top=0,end=1; dis[S]=que[0]=0; while(top!=end){ int now=que[top++];top%=maxn; for(int i=head[now];i;i=e[i].next){ if(e[i].cap&&dis[e[i].to]<dis[now]+e[i].cost){ pre[e[i].to]=i; dis[e[i].to]=dis[now]+e[i].cost; if(!inq[e[i].to]){ inq[e[i].to]=true; que[end++]=e[i].to;end%=maxn; } } } inq[now]=false; } return dis[T]!=-INF; } void dfs(){ int d=INF; for(int i=pre[T];i;i=pre[e[i].pre]) d=min(d,e[i].cap); for(int i=pre[T];i;i=pre[e[i].pre]) { e[i].cap-=d; e[i^1].cap+=d; ans+=e[i].cost*d; } } int main(){ //#define test #ifdef test freopen("in.txt","r",stdin);freopen("out.txt","w",stdout); #endif IO; cin>>n>>m; rep(i,1,n){ rep(j,1,m){ cin>>g[i][j]; } } S=0,T=3*n*m; int gap=n*m+2; nume=1; add(S,1,2,0); add(m*n,T,2,0); rep(i,1,n){ rep(j,1,m){ int s=(i-1)*m+j; if(j<m||i<n){ if(i==1&&j==1) add(s,s+gap,2,0); else add(s,s+gap,1,g[i][j]); if(j<m) add(s+gap,s+1,1,0); if(i<n) add(s+gap,s+m,1,0); } } } ans=0; while(spfa()) { dfs(); } cout<<abs(ans)<<endl; #ifdef test fclose(stdin);fclose(stdout);system("out.txt"); #endif return 0; }