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