1. 程式人生 > >洛谷P2765 魔術球問題

洛谷P2765 魔術球問題

魔術 nic vector 表頭 truct 基礎 spa rev back

題目鏈接:https://www.luogu.org/problemnew/show/P2765

知識點:  最大流

解題思路:

  本題所有邊的容量均為 \(1\)。

  從 \(1\) 開始加入數字,將這個數拆成兩個點:\(P_1\) 連源點,\(P_2\) 連匯點,然後枚舉所有比它小並且與它加起來是完全平方數的正整數 \(Num\) ,從 \(Num\) 的 \(P_1\) 連一條邊到目前要加入的數字的 \(P_2\)。

  建完邊後在之前的殘量網絡的基礎上跑 \(Dinic\),如果沒有新的流量通過,說明需要用新的柱子來放新加入的數,將新加入的數字作為新的鏈表的鏈表頭。

  當需要用的柱子數大於 \(n\) 時,停止加入數字,利用跑 \(Dinic\) 的過程中建立起來的鏈表輸出答案。

AC代碼:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int MAXN=10000;
 4 const int INF=0x3f3f3f3f;
 5 
 6 struct edge{
 7     int to,cap,rev;
 8 };
 9 int next_pt[MAXN];
10 vector<edge> G[MAXN];
11 bool used[MAXN];
12 void add_edge(int from,int to,int cap){
13     G[from].push_back((edge){to,cap,G[to].size()});
14 G[to].push_back((edge){from,0,G[from].size()-1}); 15 } 16 int dfs(int v,int t,int f){ 17 if(v==t) return f; 18 used[v]=true; 19 for(int i=0;i<G[v].size();i++){ 20 edge &e=G[v][i]; 21 if(!used[e.to] && e.cap>0){ 22 int d=dfs(e.to,t,min(f,e.cap));
23 if(d>0){ //d>0,代表有新的流量註入 24 e.cap-=d; 25 G[e.to][e.rev].cap+=d; 26 next_pt[v/2]=e.to/2; //用鏈表記錄下一個數 27 return d; 28 } 29 } 30 } 31 return 0; 32 } 33 int max_flow(int s,int t){ 34 int flow=0; 35 for(;;){ 36 memset(used,0,sizeof(used)); 37 int f=dfs(s,t,INF); 38 if(f==0) return flow; 39 flow+=f; 40 } 41 } 42 bool vis[MAXN]; 43 int head[100]; 44 int main(){ 45 int n; 46 scanf("%d",&n); 47 int s=0,t=MAXN-1; 48 int max_num=0,had=0; 49 while(had<=n){ 50 max_num++; 51 add_edge(s,max_num<<1,1); //P1 52 add_edge(max_num<<1|1,t,1); //P2 53 for(int i=1;;i++){ 54 if(i*i>max_num){ 55 int tmp=i*i-max_num; 56 if(tmp>=max_num) break; 57 add_edge(tmp<<1,max_num<<1|1,1); 58 } 59 } 60 if(!max_flow(s,t)){ 61 had++; 62 head[had]=max_num; 63 } 64 } 65 printf("%d\n",max_num-1); 66 for(int i=1;i<=n;i++){ 67 if(!vis[head[i]]){ 68 for(int j=head[i];j!=0&&j!=t/2;j=next_pt[j]){ 69 vis[j]=true; 70 printf("%d ",j); 71 } 72 puts(""); 73 } 74 } 75 76 return 0; 77 }

洛谷P2765 魔術球問題