题目链接
解析
(dalao)一看这题就知道是个最大权闭合子图
然而蒟蒻我看了题解才知道
然后一搜才知道以前写过的某些最小割叫最大权闭合子图,原来是有套路的……
首先观察题目,发现如果收益中有(d_{i,j}),那么也一定有(d_{i - 1, j})和(d_{i, j - 1}),因为每次选择只能是连续区间
我们给每个区间([i, j])建一个点,它的权值为(d_{i, j}),并从它向([i + 1, j]),([i, j - 1])两个点连边,表示选了([i, j])就必须选([i + 1, j])和([i, j - 1])
每个代号(x)的花费可以拆成两部分:(mx^2)和(cx)
前一部分只和这个代号选没选有关,所以每个代号建一个点,权值为(-mx^2),每个([i, i])向它的代号(a[i])连边,表示选了(i)就必选它的代号
后一部分和选的寿司种类有关,假设选了([i, i]),那么会增加(a[i])的花费,所以可以把([i, i])这个点的权值减去(a[i])
上面建出的是原图,然后按最大权闭合子图的套路转化成最小割即可,这里就不写怎么转化了……
P.S.
题面巨长,变量的关系需要好好捋一捋……
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define id(x, y) (((x) - 1) * N + (y))
typedef long long LL;
const int inf = 0x3f3f3f3f;
struct Graph {
struct Edge {
int v, next, cap;
Edge(int _v = 0, int _n = 0, int _c = 0):v(_v), next(_n), cap(_c) {}
};
std::vector<Edge> edge;
int cnt, head[11500], cur[11500], dep[11500];
void init() { memset(head, -1, sizeof head); cnt = 0; }
void add_edge(int u, int v, int c) { edge.push_back(Edge(v, head[u], c)); head[u] = edge.size() - 1; }
void insert(int u, int v, int c) { add_edge(u, v, c); add_edge(v, u, 0); }
bool bfs();
int dfs(int, int);
int Dinic();
} G;
int a[105], d[105][105];
int N, M, ans;
int main() {
G.init();
std::ios::sync_with_stdio(false);
std::cin >> N >> M;
for (int i = 1; i <= N; ++i) std::cin >> a[i];
for (int i = 1; i <= N; ++i) for (int j = i; j <= N; ++j) std::cin >> d[i][j];
for (int i = 1; i <= N; ++i) for (int j = i + 1; j <= N; ++j) { G.insert(id(i, j), id(i, j - 1), inf); G.insert(id(i, j), id(i + 1, j), inf); }
for (int i = 1; i <= N; ++i) G.insert(id(i, i), id(N, N) + a[i], inf);
for (int i = 1; i <= 1000; ++i) G.insert(id(N, N) + i, id(N, N) + 1001, M * i * i);
for (int i = 1; i <= N; ++i) G.insert(id(i, i), id(N, N) + 1001, a[i]);
for (int i = 1; i <= N; ++i) for (int j = i; j <= N; ++j)
if (d[i][j] > 0) G.insert(0, id(i, j), d[i][j]), ans += d[i][j];
else if (d[i][j] < 0) G.insert(id(i, j), id(N, N) + 1001, -d[i][j]);
std::cout << ans - G.Dinic() << std::endl;
return 0;
}
bool Graph::bfs() {
int que[11500], hd = 0, tl = 1;
memset(dep, -1, sizeof dep);
que[0] = dep[0] = 0;
while (hd < tl) {
int p = que[hd++];
for (int i = head[p]; ~i; i = edge[i].next)
if (edge[i].cap && !(~dep[edge[i].v])) {
dep[edge[i].v] = dep[p] + 1;
que[tl++] = edge[i].v;
}
}
return ~dep[id(N, N) + 1001];
}
int Graph::dfs(int u, int max_flow) {
if (u == id(N, N) + 1001) return max_flow;
int res = 0;
for (int &i = cur[u]; ~i; i = edge[i].next)
if (edge[i].cap && dep[edge[i].v] == dep[u] + 1) {
int d = dfs(edge[i].v, std::min(edge[i].cap, max_flow - res));
res += d, edge[i].cap -= d, edge[i ^ 1].cap += d;
if (res == max_flow) break;
}
if (!res) dep[u] = -1;
return res;
}
int Graph::Dinic() {
int res = 0;
while (bfs()) {
memcpy(cur, head, sizeof head);
res += dfs(0, inf);
}
//std::cout << res << std::endl;
return res;
}
//Rhein_E