1. 程式人生 > >[網絡流24題]魔術球問題

[網絡流24題]魔術球問題

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題]魔術球問題