[网络流24题]魔术球问题
Description
假设有$n$根柱子,现要按下述规则在这$n$根柱子中依次放入编号为$1,2,3,...$的球。
$1.$每次只能在某根柱子的最上面放球。
$2.$在同一根柱子中,任何$2$个相邻球的编号之和为完全平方数。
求在$n$根柱子上最多能放多少个球。
Input
第$1$行有$1$个正整数$n$,表示柱子数。
Output
第一行是球数。
接下来的$n$行,每行是一根柱子上的球的编号。
Sample Input
4
Sample Output
11
1 8
2 7 9
3 6 10
4 5 11
HINT
$n\;\leq\;100$
Solution
枚举答案$ans$,在图中建立节点$1,2,...,ans$。如果对于$i,j,\;i<j$且$i+j$为一个完全平方数,建一条有向边$(i,j)$。该图是有向无环图,求最小路径覆盖。寻找最大的$ans$使得最小路径覆盖数$=n$。(枚举$ans$即可)
#include<cmath> #include<ctime> #include<queue> #include<stack> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define N 5005 #define M 200001 using namespace std; struct graph{ int nxt,to,f; }e[M]; int a[N<<1],g[N<<1],dep[N<<1],l,m,s,t,fl,ans,cnt=1; bool v[N]; queue<int> q; inline bool sq(int x){ int k=sqrt(x); return k*k==x; } inline void addedge(int x,int y,int f){ e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].f=f; } inline void adde(int x,int y,int f){ addedge(x,y,f);addedge(y,x,0); } inline bool bfs(int u){ memset(dep,0,sizeof(dep)); q.push(u);dep[u]=1; while(!q.empty()){ u=q.front();q.pop(); for(int i=g[u];i;i=e[i].nxt) if(e[i].f>0&&!dep[e[i].to]){ q.push(e[i].to); dep[e[i].to]=dep[u]+1; } } return dep[t]; } inline int dfs(int u,int f){ int ret=0; if(u==t) return f; for(int i=g[u],d;i&&f;i=e[i].nxt) if(e[i].f>0&&dep[e[i].to]>dep[u]){ d=dfs(e[i].to,min(e[i].f,f)); e[i].f-=d;e[i^1].f+=d;f-=d;ret+=d; } return ret; } inline void dinic(){ while(true){ if(!bfs(s)) return; fl+=dfs(s,N); } } inline bool chk(int x){ dinic(); return x-fl<=m; } inline void find(int u){ a[++l]=u; for(int i=g[u];i;i=e[i].nxt) if(e[i].to!=s&&!v[e[i].to-N]&&!e[i].f){ v[e[i].to-N]=true;find(e[i].to-N); } } inline void Aireen(){ scanf("%d",&m);ans=m; s=N-1;t=(N<<1)-1; for(int j=1;j<=ans;++j){ adde(s,j,1);adde(j+N,t,1); for(int i=1;i<j;++i) if(sq(i+j)) adde(i,j+N,1); } do{ ++ans; adde(s,ans,1);adde(ans+N,t,1); for(int i=1;i<ans;++i) if(sq(i+ans)) adde(i,ans+N,1); }while(chk(ans)); printf("%d\n",ans-1); v[ans]=0; for(int i=1;i<ans;++i) if(!v[i]){ l=0;v[i]=true;find(i); for(int j=1;j<=l;++j) printf("%d ",a[j]); printf("\n"); } } int main(){ freopen("ball.in","r",stdin); freopen("ball.out","w",stdout); Aireen(); fclose(stdin); fclose(stdout); return 0; }