最大權閉合子圖-最小割-網絡流

題目鏈接:這裡傳送

  題目大意:給定一個n個數的序列,標號為1~n,有正有負,可以無數次操作:刪去一些數,條件是刪去編號為i的數同時,所有編號是i的整數倍的數都要被刪去。求剩下的數的和最大時的和,即剩下的sum最大。

  解題思路:典型的最大權閉合子圖問題,有關知識的詳細可參考:我覺得最能看懂的博文

  簡要介紹:如果選一個x,就必須要選另一個y,也就是綁定的話,建從x到y的容量為INF的邊,然後從S向所有正值x的點建容量為x的邊,從所有負值x向T建容量為|x|的邊,然後跑從S到T的最小割(最大流),用原序列的所有正值的和sum來減去最小割,得到的就是最大權閉合子圖的權值,也就是結果。

  本題的思想轉化為,如果要保留編號為i個數,就必須保留所有編號為i的因子的數,也就是對第i個數,找出所有存在的編號為i的整數倍k*i的數,建從k*i到i的INF的邊。

以下是AC代碼:

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=2e3;
  4 const int INF=0x3f3f3f3f;
  5 int n;
  6 struct Dinic
  7 {
  8     struct Edge
  9     {
 10         int from, to, cap, flow;
 11         Edge(int u, int v, int c, int f)
 12             : from(u), to(v), cap(c), flow(f) {}
 13     };
 14     int n, m, s, t;
 15     vector<Edge> edges;
 16     vector<int> G[N];
 17     bool vis[N];
 18     int d[N];
 19     int cur[N];
 20     void init(int n)
 21     {
 22         this->n = n;
 23         for (int i = 0; i < n; i++) G[i].clear();
 24         edges.clear();
 25     }
 26     void AddEdge(int from, int to, int cap)
 27     {
 28         edges.emplace_back(from, to, cap, 0);
 29         edges.emplace_back(to, from, 0, 0);
 30         m = edges.size();
 31         G[from].push_back(m - 2);
 32         G[to].push_back(m - 1);
 33     }
 34     bool BFS()
 35     {
 36         memset(vis, 0, sizeof(vis));
 37         memset(d, 0, sizeof(d));
 38         queue<int> q;
 39         q.push(s);
 40         d[s] = 0;
 41         vis[s] = 1;
 42         while (!q.empty())
 43         {
 44             int x = q.front();
 45             q.pop();
 46             for (int i = 0; i < G[x].size(); i++)
 47             {
 48                 Edge& e = edges[G[x][i]];
 49                 if (!vis[e.to] && e.cap > e.flow)
 50                 {
 51                     vis[e.to] = 1;
 52                     d[e.to] = d[x] + 1;
 53                     q.push(e.to);
 54                 }
 55             }
 56         }
 57         return vis[t];
 58     }
 59     int DFS(int x, int a)
 60     {
 61         if (x == t || a == 0) return a;
 62         int flow = 0, f;
 63         for (int& i = cur[x]; i < G[x].size(); i++)
 64         {
 65             Edge& e = edges[G[x][i]];
 66             if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0)
 67             {
 68                 e.flow += f;
 69                 edges[G[x][i] ^ 1].flow -= f;
 70                 flow += f;
 71                 a -= f;
 72                 if (a == 0) break;
 73             }
 74         }
 75         return flow;
 76     }
 77     int Maxflow(int s, int t)
 78     {
 79         this->s = s, this->t = t;
 80         int flow = 0;
 81         while (BFS())
 82         {
 83             memset(cur, 0, sizeof(cur));
 84             flow += DFS(s, INF);
 85         }
 86         return flow;
 87     }
 88 } solver;
 89 
 90 int main()
 91 {
 92     scanf("%d",&n);
 93     solver.init(n+3);
 94     int s=0,t=n+1,x,sum=0;
 95     for(int i=1;i<=n;++i)
 96     {
 97         scanf("%d",&x);
 98         if(x<0)
 99             solver.AddEdge(i,t,-x);
100         else
101         {
102             solver.AddEdge(s,i,x);
103             sum+=x;
104         }
105         for(int j=2;i*j<=n;++j)
106             solver.AddEdge(i*j,i,INF);
107     }
108     printf("%d
",sum-solver.Maxflow(s,t));
109 }
原文地址:https://www.cnblogs.com/Lin88/p/10015583.html