题目
解题思路
前置知识 :最大全闭合子图
不了解的可以先自行学习一下,这里简略的讲一下最大权闭合子图算法解决的问题。
最大权闭合子图大概讲的是求一种依赖图的最大权值。如果一个点选了,那么它的依赖点也必须选。让你选择一个满足依赖关系的子集,使得权值最大。
依赖图的建立
首先如果种类为x的寿司吃了
c
(
c
>
0
)
c(c>0)
c(c>0)个,要付出的代价是
m
x
2
+
c
x
mx^2 + cx
mx2+cx。 我们建立一个种类节点,这个节点的代价是
m
x
2
mx^2
mx2。每个种类为x的寿司向这个点建立依赖边。这样当
c
>
0
c>0
c>0时,这个点必定被选择。同时,每个寿司的代价是
x
x
x,这样就解决了
c
x
cx
cx的问题。
其次每次必须选择连续区间,如果
[
l
,
r
]
[l,r]
[l,r]区间的寿司被选择了,那么会获得
q
l
,
r
q_{l,r}
ql,r的美味度。 显然,如果某两次选的区间有交集,我们可以合并为一次。然后我们分两种情况:如果
l
=
r
l=r
l=r,那么
q
l
,
r
q_{l,r}
ql,r依赖第
l
l
l个寿司。如果
l
!
=
r
l!=r
l!=r,那么
q
l
,
r
q_{l,r}
ql,r依赖
q
l
+
1
,
r
q_{l + 1,r}
ql+1,r和
q
l
,
r
−
1
q_{l,r - 1}
ql,r−1。
解决
我们可以根据题意建立依赖图模型,并通过最大权闭合子图算法将依赖图最大权问题转化为最大流问题加以解决。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 1e5 + 100;
const ll INF = 1e16;
struct edge {
int to, rev;
ll cap;
edge() {}
edge(int to, ll cap, int rev) :to(to), cap(cap), rev(rev) {}
};
vector<edge> G[N];
int iter[N], level[N];
void addedge(int from, int to, ll cap) {
G[from].push_back(edge(to, cap, G[to].size()));
G[to].push_back(edge(from, 0, G[from].size() - 1));
}
void bfs(int s) {
memset(level, -1, sizeof(level));
level[s] = 0;
queue<int> Q;
Q.push(s);
while (!Q.empty()) {
int u = Q.front(); Q.pop();
for (edge e : G[u]) {
if (e.cap > 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 = iter[u]; i < G[u].size(); i++) {
edge &e = G[u][i];
if (e.cap > 0 && level[e.to] > level[u]) {
ll d = dfs(e.to, t, min(e.cap, f));
if (d > 0) {
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
ll max_flow(int s, int t) {
ll ans = 0, f;
while (true) {
bfs(s);
if (level[t] < 0) return ans;
memset(iter, 0, sizeof(iter));
while ((f = dfs(s, t, INF)) > 0)
ans += f;
}
}
int n, m, tot, tp;
int sa[110], has[110], id[110][110], w[110][110];
int main() {
//freopen("0.txt", "r", stdin);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", sa + i), has[i] = sa[i];
sort(has + 1, has + n + 1);
tp = unique(has + 1, has + n + 1) - has - 1;
for (int i = 1; i <= n; i++) sa[i] = lower_bound(has + 1, has + tp + 1, sa[i]) - has;
tot = tp + n;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
scanf("%d", &w[i][j]);
id[i][j] = ++tot;
}
}
ll S = ++tot, T = ++tot, res = 0;
for (int i = 1; i <= tp; i++) addedge(i, T, 1LL * m * has[i] * has[i]);
for (int i = 1; i <= n; i++) {
addedge(tp + i, sa[i], INF);
addedge(tp + i, T, has[sa[i]]);
}
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
if (w[i][j] > 0) {
res += w[i][j];
addedge(S, id[i][j], w[i][j]);
}
else
addedge(id[i][j], T, -w[i][j]);
if (i == j) addedge(id[i][j], tp + i, INF);
else {
addedge(id[i][j], id[i + 1][j], INF);
addedge(id[i][j], id[i][j - 1], INF);
}
}
}
printf("%lld\n", res - max_flow(S, T));
return 0;
}