洛谷P2756 飞行员配对方案问题
分析
其实就是一个二分图匹配求最大匹配数的问题,加一个源点和汇点,再跑一遍网络流,输出方案的时候检查一下有没有流经过即可(反向边是否非0)。
注意:每个点在向源点和汇点连边时,要在输入完之后再连。否则会重复连。
#include<bits/stdc++.h> using namespace std; #define N 205 #define M 30005 int to[M],head[N],nex[M],w[M],lev[N],tot=1,cnt=0,inf=0x7f7f7f,s=0,t=101,n,cur[N]; struct node{ int u,v,bian; }e[M]; queue<int> q; void add(int a,int b,int ww) { tot++; to[tot]=b; w[tot]=ww; nex[tot]=head[a]; head[a]=tot; if(ww==0&&b&&a!=t) e[cnt].bian=tot; } bool bfs() { memset(lev,-1,sizeof(lev)); lev[s]=0; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=nex[i]) { int v=to[i]; if(w[i]>0&&lev[v]==-1) lev[v]=lev[u]+1,q.push(v); } } if(lev[t]!=-1) return true; return false; } int dfs(int u,int flow) { if(u==t) return flow; int ret=flow; for(int i=cur[u];i!=-1;i=nex[i]) { cur[u]=i; if(ret<=0) break; int v=to[i]; if(w[i]>0&&lev[u]+1==lev[v]) { int k=dfs(v,min(w[i],ret)); ret-=k; w[i]-=k; w[i^1]+=k; //异或运算:2k^1=(2k+1) (2k+1)^1=2k; } } return flow-ret;//最大限制 - 还剩的多少没流 =流出去了多少 } void dinic() { int ans=0; while(bfs()){ for(int i=0;i<=101;i++) cur[i]=head[i]; ans+=dfs(s,inf); } printf("%d\n",ans); } int main() { int m,a,b; scanf("%d%d",&m,&n); memset(head,-1,sizeof(head)); while(1){ scanf("%d%d",&a,&b); if(a==-1&&b==-1) break; cnt++; add(a,b,inf); add(b,a,0); e[cnt].u=a; e[cnt].v=b; } //一定要在后面建,否则会重复建边 for(int i=1;i<=m;i++) add(s,i,1),add(i,s,0); for(int i=m+1;i<=n;i++) add(i,t,1),add(t,i,0); dinic(); for(int i=1;i<=cnt;i++) if(w[e[i].bian]!=0) printf("%d %d\n",e[i].u,e[i].v); } /* 5 10 1 7 1 8 2 6 2 9 2 10 3 7 3 8 4 7 4 8 5 10 -1 -1 2 4 1 3 1 4 2 3 -1 -1 */
洛谷P2765 魔术球问题
«问题描述:
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。
n<=55
分析
首先,这道题的主要限制在于球(平方数),考虑对球建边。将每个球拆成两部分,第一部分对 s 连边,第二部分对 t 连边, 两部分间通过和为平方数的条件连边(边权都为1)。一个一个地加球,加一次跑一次最大流,如果在以前的基础上产生了新流,说明加入的这个球能对已有的球产生平方和的联系,即可以加在某一个球上面。加球一直到没有产生新流了,说明需要加柱子了,这时就将柱子++,一直到达到n个为止。
方案输出
每当新加一个柱子时,记录最先放入柱子的球,然后在找增广路的时候对每一个点,记录下一个可行的点是谁,类似于邻接表一样的链表式结构。然后输出的时候枚举柱子。
#include<bits/stdc++.h> using namespace std; #define inf 2100000000 #define M 1000005 int to[M],nex[M],head[M],w[M],tot=0,vis[M]; int nexxt[M],headd[M],lev[M],cur[M],s=M-5,t=M-4; queue<int> q; void add(int a,int b,int ww) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=ww; to[++tot]=a; nex[tot]=head[b]; head[b]=tot; w[tot]=0; } bool bfs() { memset(lev,-1,sizeof(lev)); lev[s]=0; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=nex[i]){ int v=to[i]; if(lev[v]==-1 && w[i]) lev[v]=lev[u]+1,q.push(v); } } return lev[t]!=-1; } int dfs(int u,int flow) { if(u==t) return flow; int ret=flow; for(int i=head[u];i!=-1;i=nex[i]){ int v=to[i]; if(ret<=0) break; if(lev[v]==lev[u]+1&&w[i]>0){ int k=dfs(v,min(ret,w[i])); nexxt[u>>1]=v>>1; //记录一下这个球对应的下一个球是哪一个 因为存的时候*2+1了 所以要/2 不需要分奇偶讨论 ret-=k; w[i]-=k; w[i^1]+=k; } } return flow-ret; } int dinic() { int ans=0; while(bfs()){ ans+=dfs(s,inf); } return ans; } int main() { int n; scanf("%d",&n); memset(head,-1,sizeof(head)); int now=0,zhu=0;// headd[1]=1; while(zhu<=n){ now++; add(s,now<<1,1); add(now<<1|1,t,1); for(int i=sqrt(now)+1;i*i<now*2;i++) add((i*i-now)<<1,now<<1|1,1); int tmp=dinic(); if(!tmp) headd[++zhu]=now; } printf("%d\n",now-1); for(int i=1;i<=n;i++) if(!vis[headd[i]]){ for(int j=headd[i];j&&j!=t>>1;j=nexxt[j]) printf("%d ",j),vis[j]=true; printf("\n"); } }