ccf2020年6月第5題喬喬和牛牛逛超市 100
阿新 • • 發佈:2020-12-12
題目
這道題設計到了最大權閉合子圖,
[https://www.cnblogs.com/dilthey/p/7565206.html]:
求最小割時又涉及到了網路流問題,先看了以前的離散數學,然後看了這篇部落格
[https://blog.csdn.net/ztf312/article/details/78710345]:
對於求解網路流反向邊的作用可以參考這兩篇部落格
[http://www.wutianqi.com/blog/3107.html]: “最大流 — Edmond Karp演算法”
簡單來說,跟回溯類似吧,彌補最優解。
最後寫題時,看了幾篇部落格,總體思想都是一樣的,相對來說,這篇部落格應該是最好看懂的。
[https://blog.csdn.net/bcqccb/article/details/108332032]:
最後也是自己寫了一次,和上面部落格整體結構相似。
#include<bits/stdc++.h> using namespace std; #define LL long long #define INF 0x3f3f3f3f3f3f3f3f struct edge{ int to; LL weight; int pos;//記錄改變在節點儲存邊的vector中的序號 }; const int N = 2e4 + 5, M = 2e5 + 5; vector<edge> G[N]; int level[N]; int pos_num[N]; void add(int from, int to, LL w) { G[from].push_back({to, w,(int) G[to].size()}); G[to].push_back({from, 0, (int) G[from].size() - 1});//這是為了求最大流記錄記錄from流向to的流量,方便回溯,彌補最優解 } LL gety(int x, int a, int b, int c) { return a * x * x + b * x + c; } LL cal(int l, int r, int a, int b, int c) { if(l > r) return 0; double x = - b / 2.0 / a; if(a >= 0){ return max(gety(l, a, b, c), gety(r, a, b, c)); } else{ if(l > x) return gety(l, a, b, c); else if(r < x) return gety(r, a, b, c); else return max(gety(floor(x), a, b, c), gety(ceil(x), a, b, c)); } } bool bfs() { memset(level, -1, sizeof(level)); queue<int> q; level[0] = 0; q.push(0); while(! q.empty()) { int u = q.front(); q.pop(); for(int i = 0; i < G[u].size(); i ++){ edge &e = G[u][i]; if(e.weight > 0 && level[e.to] < 0){ level[e.to] = level[u] + 1; q.push(e.to); } } } } LL dfs(int u, int t, LL f) { if(u == t) return f; for(int &i = pos_num[u]; i < G[u].size(); i ++){ edge &e = G[u][i]; if(e.weight > 0 && level[u] + 1 == level[e.to]){ LL w = dfs(e.to, t, min(f, e.weight)); if(w > 0){ e.weight -= w; G[e.to][e.pos].weight += w; return w; } } } return 0; } LL max_flow(int s, int t) { LL flow = 0; while(1){ bfs(); if(level[t] < 0) return flow; memset(pos_num, 0, sizeof(pos_num)); LL f = 0; while((f = dfs(s, t, INF)) > 0) flow += f; } return 0; } int main() { int n, m; cin>>n>>m; LL weight_sum = 0; int T = 2 * n + 1;//匯聚節點 for(int i = 1; i <= n; i ++){ int l, r, a, b, c; cin>>l>>r>>a>>b>>c; //計算權值,不包括邊界點 LL w1 = cal(l + 1, r - 1, a, b, c); //計算邊界點 LL w2 = max(gety(l, a, b, c), gety(r, a, b, c)) - w1; //cout<<"w1:"<<w1<<"w2:"<<w2<<endl; if(w1 > 0){ weight_sum += w1; add(0, i, w1); } else{ add(i, T, -w1); } if(w2 > 0){ weight_sum += w2; add(0, i + n, w2); } else{ add(i + n, T, -w2); } add(i + n, i, INF); } for(int i = 0; i < m; i ++){ int z, x, y; cin>>z>>x>>y; if(z == 1){ add(y, x, INF); } else { add(y + n, x, INF); } } cout<<weight_sum - max_flow(0, T)<<endl; }
寫完這題,感想挺多的,後天就考試了,沒一點把握,尤其是這道題涉及到的演算法和思想是以前學了的,挺後悔大一沒好好學數學和大二沒有認真學演算法,emmmm,加油吧!!!
祝我好運!!!