假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。
题解:
- 先将其转化为一张图 并把能连的边都连上 数字小的连数字大的,因为从小到大放入
- 可以将其转化为最小路径覆盖 每条路径就是每个石柱上的一串数字
- 显然当最少路径大于石柱个数的时候 不能再放了
- 不断更新二分图即可
- 注意每次跑的是残量网络 所以累和即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f3f const int N=1e5+10; const int M=1e5+10; struct Edge { int to, next, w; } edge[M<<1]; int head[N],cur[N],pos=1,level[N]; void add(int a, int b, int c) { edge[++pos] = (Edge){b, head[a], c};head[a] = pos; edge[++pos] = (Edge){a, head[b], 0};head[b] = pos; } bool bfs(int s, int t) { memset(level, 0, sizeof level); queue<int> q; level[s] = 1; q.push(s); while (!q.empty()) { int pos = q.front();q.pop(); for (int i = head[pos]; i; i = edge[i].next) { int v = edge[i].to; if (!edge[i].w || level[v]) continue; level[v] = level[pos] + 1; q.push(v); } } return level[t]; } int dfs(int s, int t, int flow) { if(s==t||flow==0)return flow; int f,ret = 0; for (int &i = cur[s],v; i; i = edge[i].next) { v = edge[i].to; if (level[v] == level[s] + 1 && (f=dfs(v,t,min(flow,edge[i].w)))>0) { edge[i].w -= f; edge[i ^ 1].w += f; flow -= f; ret += f; if(!flow)break; } } return ret; } int dinic(int s, int t) { int ret = 0; while (bfs(s, t)) memcpy(cur,head,sizeof cur),ret += dfs(s, t, inf); return ret; } int spr[N],s,t,n,vis2[N],to[N]; int main() { cin>>n;s=0,t=N-1;int T=10000; for(int i=1;i<=10000;i++)spr[i]=i*i; int pos=1,sum=0; while(1) { add(s,pos,1);add(pos+T,t,1); int x=lower_bound(spr+1,spr+1+1000,pos)-spr; for(int j=2*x;j>=1;j--) { int temp=spr[j]-pos; if(temp>0&&temp<pos)add(temp,pos+T,1); } sum+=dinic(s,t); if(pos-sum>n)break; pos++; } pos--; cout<<pos<<endl; for(int i=1;i<=pos;i++) for(int j=head[i];j;j=edge[j].next) if(!edge[j].w&&edge[j].to!=s) { to[i]=edge[j].to-T;break; } for(int i=1;i<=pos;i++) { if(vis2[i])continue; for(int k=i;k;k=to[k]) vis2[k]=1,printf("%d ",k); printf(" "); } }