拆点的另一个方法。。: 把这个点 i 拆成 i<<1 和 ( i<<1) +1; 因为上限无穷所以这样会比较方便点。。。然后就是枚举时候不要二分。。。那样会重新构图的。。。顺序枚举可以用到回退边的流量来恢复构建的图。。。然后结合给的讲解就可以写出来了。。。。
【问题分析】
枚举答案转化为判定性问题,然后最小路径覆盖,可以转化成二分图最大匹配,从而用最大流解决。
【建模方法】
枚举答案A,在图中建立节点1..A。如果对于i<j有i+j为一个完全平方数,连接一条有向边(i,j)。该图是有向无环图,求最小路径覆盖。如果刚好满足最小路径覆盖数等于N,那么A是一个可行解,在所有可行解中找到最大的A,即为最优解。
具体方法可以顺序枚举A的值,当最小路径覆盖数刚好大于N时终止,A-1就是最优解。
【建模分析】
由于是顺序放球,每根柱子上的球满足这样的特征,即下面的球编号小于上面球的编号。抽象成图论,把每个球看作一个顶点,就是编号较小的顶点向编号较大的顶点连接边,条件是两个球可以相邻,即编号之和为完全平方数。每根柱子看做一条路径,N根柱子要覆盖掉所有点,一个解就是一个路径覆盖。
最小路径覆盖数随球的数量递增不递减,满足单调性,所以可以枚举答案(或二分答案),对于特定的答案求出最小路径覆盖数,一个可行解就是最小路径覆盖数等于N的答案,求出最大的可行解就是最优解。本问题更适合枚举答案而不是二分答案,因为如果顺序枚举答案,每次只需要在残量网络上增加新的节点和边,再增广一次即可。如果二分答案,就需要每次重新建图,大大增加了时间复杂度。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define inf 1<<30 #define ext 2000 #define N 200000 #define M 4005 #define cc(m,v) memset(m,v,sizeof(m)) struct node{ int u,v,f,next; }edge[N]; int head[M],p,lev[M],cur[M]; int que[N],squ[M],vis[M]; void ainit(){ p=0,cc(head,-1),cc(vis,0),vis[0]=vis[1]=1; } void addedge(int u,int v,int f){ edge[p].u=u,edge[p].v=v,edge[p].f=f,edge[p].next=head[u],head[u]=p++; edge[p].u=v,edge[p].v=u,edge[p].f=0,edge[p].next=head[v],head[v]=p++; } bool bfs(int s,int t){ int i,v,u,qin=0,qout=0; cc(lev,-1),lev[s]=0,que[qin++]=s; while(qout!=qin){ u=que[qout++]; for(i=head[u];i!=-1;i=edge[i].next) if(edge[i].f>0 && lev[v=edge[i].v]==-1){ lev[v]=lev[u]+1,que[qin++]=v; if(v==t) return 1; } } return 0; } int dinic(int s,int t){ int i,k,f,u,qin; int flow=0; while(bfs(s,t)){ memcpy(cur,head,sizeof(head)); u=s,qin=0; while(1){ if(u==t){ for(k=0,f=inf;k<qin;k++) if(edge[que[k]].f < f) f=edge[que[i=k]].f; for(k=0;k<qin;k++) edge[que[k]].f-=f,edge[que[k]^1].f+=f; flow+=f,u=edge[que[qin=i]].u; } for(i=cur[u];cur[u]!=-1;i=cur[u]=edge[cur[u]].next) if(edge[i].f>0 && lev[u]+1==lev[edge[i].v]) break; if(cur[u]!=-1) que[qin++]=cur[u],u=edge[cur[u]].v; else{ if(qin==0) break; lev[u]=-1,u=edge[que[--qin]].u; } } } return flow; } void dfs(int u){ int v; vis[u]=1; for(int i=head[u];i!=-1;i=edge[i].next) if(edge[i].f==0 && !vis[v=edge[i].v] && edge[i].v>1){ printf(" %d",(v-1)>>1); dfs(v-1); return; } } int main(){ int n,i,j,k,s,t,ans; cc(squ,0); for(i=1;i*i<M;i++) squ[i*i]=1; while(scanf("%d",&n)!=-1){ ainit(); s=0,t=1; for(i=1;i<M ;i++){ addedge(s,i<<1,1),addedge((i<<1)+1,t,1); for(j=1;j<i;j++) if(squ[j+i]) addedge(j<<1,(i<<1)+1,1); for(k=0;k<p;k+=2) edge[k].f+=edge[k^1].f,edge[k^1].f=0; if(i-dinic(s,t)>n) break; } printf("%d\n",i-1); ainit();ans=i-1; for(i=1;i<=ans;i++){ addedge(s,i<<1,1),addedge((i<<1)+1,t,1); for(j=1;j<i;j++) if(squ[j+i]) addedge(j<<1,(i<<1)+1,1); } dinic(s,t); for(j=1;j<=ans;j++) if(!vis[j<<1]){ printf("%d",j); dfs(j<<1); printf("\n"); } } return 0; }