題目鏈接:這裡傳送
題目大意:給定一個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\n",sum-solver.Maxflow(s,t)); 109 }
原文地址:https://www.cnblogs.com/Lin88/p/10015583.html
时间: 2024-07-30 03:19:52