題意:
若干區間,區間有權值,選擇一個子集,使得權值和儘量大並且每個點不被覆蓋超過 \(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;
}