[网络流24题]魔术球问题(简化版
对于可以相邻的点 u->v 连接一条边 ui->vj,对于那么完全放完这些点需要的柱子就是最小路径覆盖的模型。
因为要求最多n根柱子最多可以放多少数,一个方法是二分答案跑最大流检验。
但是对于网络流题目更好的方法是从小到大枚举数,每次可以在原来的基础上增广,何时最小路径覆盖数>n就break。
程序中k代表数字上界,j代表最小路径覆盖数,每次k++,j也++是因为添加一个点默认需要一条新路径覆盖它,每次跑网络流j减去最大流即是1~k的最小路径覆盖数。
// q.c #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define mem(a) memset(a,0,sizeof(a)) using namespace std; const int M=3200+10,N=1600,INF=(int)1e9; struct Edge { int v,nex,flow,cap; Edge() {} Edge(int a,int b,int c,int d):v(a),nex(b),flow(c),cap(d) {} }ed[N*N]; int cnt,head[M]; void add_edge(int a,int b,int c) { ed[cnt]=Edge(b,head[a],0,c); head[a]=cnt++; ed[cnt]=Edge(a,head[b],0,0); head[b]=cnt++; } struct Dinic { int n,s,t,cur[M],dis[M]; queue<int> Q; Dinic():n(0),s(0),t(0) { mem(cur); mem(dis); while(!Q.empty()) Q.pop(); } bool bfs() { mem(dis); dis[s]=1; Q.push(s); int u,i; Edge e; while(!Q.empty()) { u=Q.front(); Q.pop(); for(i=head[u];i!=-1;i=ed[i].nex) { e=ed[i]; if(!dis[e.v]&&e.cap>e.flow) { dis[e.v]=dis[u]+1; Q.push(e.v); } } } return dis[t]; } int dfs(int u,int lim) { if(u==t||!lim) return lim; int f=0,tmp=0; Edge e; for(int &i=cur[u];i!=-1;i=ed[i].nex) { e=ed[i]; if(dis[e.v]==dis[u]+1) { f=dfs(e.v,min(lim,e.cap-e.flow)); if(f>0) { ed[i].flow+=f; tmp+=f; ed[i^1].flow-=f; lim-=f; if(!lim) break; } } } return tmp; } int solve(int x,int y,int z) { s=x; t=y; n=z; int ans=0; while(bfs()) { for(int i=0;i<=n;i++) cur[i]=head[i]; ans+=dfs(s,INF); } return ans; } }DC; int main() { freopen("balla.in","r",stdin); freopen("balla.out","w",stdout); int n,i,j,k,tmp,s=0,t=N+N+1; scanf("%d",&n);; memset(head,-1,sizeof(head)); for(k=1,j=1;;k++,j++) { for(i=1;i<k;i++) { tmp=(int)sqrt(0.0+i+k); if(tmp*tmp==(i+k)) add_edge(i,k+N,1); } add_edge(0,k,1); add_edge(k+N,t,1); j-=DC.solve(s,t,t); if(j>n) break; } printf("%d\n",k-1); return 0; }