洛谷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");
}
}
