洛谷P2765 魔术球问题
题目链接: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 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”