CF717G Underfail 题解
题意:
若干区间,区间有权值,选择一个子集,使得权值和尽量大并且每个点不被覆盖超过 \(x\) 次。
\(n \le 500\)
思路:
很神奇的一道题。
我们考虑费用流,如果单纯的一边是区间一边是点的话其实并不好做,所以这道题我们直接建一排 \(n+2\) 个点,一个区间 \(l, r\) 就从 \(l\) 到 \(r+1\) 连流量 \(1\) 费用 \(p\) 的边,然后 \(i \to i + 1\) 连流量 \(x\) 费用 \(0\) 的边。
显然最大流肯定是 \(x\),则 \(i \to i + 1\) 如果流了 \(f\) 个过去,说明有 \(x-f\) 个是从区间的边过去的,这就表示了区间覆盖,且显然 \(f \le x\),则方案合法。
建模过于神奇了。
点击查看代码
#include <iostream>
#include <vector>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 505;
const int inf = 0x3f3f3f3f;
struct Edge {
int to, val, cst, rev;
Edge (int _to = 0, int _val = 0, int _cst = 0, int _rev = 0) :
to(_to), val(_val), cst(_cst), rev(_rev) {}
};
vector<Edge> e[N];
void add(int u, int v, int w, int c) {
e[u].push_back(Edge(v, w, c, (int)e[v].size()));
e[v].push_back(Edge(u, 0, -c, (int)e[u].size() - 1));
}
int d[N] = {0};
bool inq[N] = {false};
bool bfs(int s, int t) {
memset(d, inf, sizeof d);
queue<int> q;
q.push(s), d[s] = 0;
while (!q.empty()) {
int h = q.front();
q.pop();
inq[h] = false;
for (auto i: e[h])
if (i.val > 0 && d[i.to] > d[h] + i.cst) {
d[i.to] = d[h] + i.cst;
if (!inq[i.to])
inq[i.to] = true, q.push(i.to);
}
}
return d[t] != inf;
}
int cur[N] = {0};
bool vis[N] = {false};
int dfs(int x, int t, int f) {
if (x == t)
return f;
int fl = 0;
vis[x] = true;
for (int i = cur[x]; i < (int)e[x].size(); i = ++cur[x])
if (e[x][i].val > 0 && !vis[e[x][i].to] && d[e[x][i].to] == d[x] + e[x][i].cst) {
fl = dfs(e[x][i].to, t, min(f, e[x][i].val));
if (fl > 0) {
e[x][i].val -= fl;
e[e[x][i].to][e[x][i].rev].val += fl;
break;
}
}
if (fl == 0)
d[x] = -1;
vis[x] = false;
return fl;
}
int Dinic(int s, int t) {
int ans = 0;
while (bfs(s, t)) {
memset(cur, 0, sizeof cur);
int ad = 0;
while ((ad = dfs(s, t, inf)) > 0)
ans += ad * d[t];
}
return ans;
}
int n, m, k;
string s, t[N];
int p[N] = {0};
int main() {
cin >> n >> s >> m;
for (int i = 1; i <= m; i++) {
cin >> t[i] >> p[i];
for (int j = 0; j + (int)t[i].size() - 1 < (int)s.size(); j++)
if (s.substr(j, (int)t[i].size()) == t[i])
add(j + 1, j + (int)t[i].size() + 1, 1, -p[i]);
}
cin >> k;
for (int i = 1; i <= n + 1; i++)
add(i, i + 1, k, 0);
cout << -Dinic(1, n + 2) << endl;
return 0;
}