[網絡流24題]魔術球問題
阿新 • • 發佈:2018-04-10
esp 覆蓋數 head i++ %d include tar 新的 amp
題目描述
將所有球點,在每根柱子上就是下邊的點向上邊的點連邊,可以連邊的條件是兩球編號之和為完全平方數。
再把這n跟柱子看做是n條路徑,問題也就轉換成了用n條路徑覆蓋所有的點,也就是最小路徑覆蓋問題。
//最小路徑覆蓋數隨著點數的增加不會遞減,滿足二分的性質,但是二分時要重新構圖,所以不如直接順序枚舉答案,這樣可以利用上次的殘量網絡,降低了復雜度。
對於新枚舉到的球,有兩種選擇:
1.放到已經有球的柱子上
2.自己單獨開辟一根柱子(不超過n)
所以將這個新的球與已經放好的球連邊(需滿足條件),重新跑一遍最大流,這時若沒有新流,則說明這個新球不能放在任何一個已經有球的柱子上。
//num數組記錄每根柱子最下邊的球的編號,用於輸出方案。
#include<complex> #include<cstdio> using namespace std; const int INF=0x3f3f3f3f; const int N=3507; struct node{ int v,f,nxt; }e[N*N]; int n,Enum=1,s,t,tot; int front[N],cur[N],deep[N],num[N],path[N]; int q[N]; bool vis[N]; void Insert(int u,int v) { e[++Enum].v=v;e[Enum].f=1;e[Enum].nxt=front[u];front[u]=Enum; e[++Enum].v=u;e[Enum].nxt=front[v];front[v]=Enum; } bool bfs() { for(int i=0;i<=t;i++) { deep[i]=0; cur[i]=front[i]; } int head=1,tail=0,u,v; deep[s]=1;q[++tail]=s; while(head<=tail) { u=q[head++];for(int i=front[u];i;i=e[i].nxt) { v=e[i].v; if(!deep[v] && e[i].f) { deep[v]=deep[u]+1; if(v==t)return 1; q[++tail]=v; } } } return 0; } int dfs(int x,int cur_flow) { if(x==t)return cur_flow; int rest=cur_flow,v; for(int &i=cur[x];i;i=e[i].nxt) { v=e[i].v; if(deep[v]==deep[x]+1 && e[i].f && rest) { int new_flow=dfs(v,min(e[i].f,rest)); e[i].f-=new_flow; e[i^1].f+=new_flow; rest-=new_flow; if(v!=t)path[x>>1]=v>>1; if(!rest)return cur_flow; } } deep[x]=0; return cur_flow-rest; } int Dinic() { int res=0; while(bfs()) res+=dfs(s,INF); return res; } int main() { scanf("%d",&n); s=0;t=N-1; int cnt=0; while(cnt<=n) { tot++; Insert(s,tot<<1);Insert(tot<<1|1,t); int tmp=sqrt(tot); for(int i=tmp+1;i*i<tot*2;i++) Insert((i*i-tot)<<1,tot<<1|1); if(!Dinic())num[++cnt]=tot; } printf("%d\n",tot-1); for(int i=1;i<=n;i++) { if(vis[num[i]])continue; int tmp=num[i]; vis[tmp]=1; while(tmp) { printf("%d ",tmp); tmp=path[tmp]; vis[tmp]=1; } puts(""); } return 0; }
[網絡流24題]魔術球問題