[BZOJ1283]序列
试题描述
输入
输出
输入示例
10 5 3 4 4 4 6 6 6 6 6 4 4
输出示例
30
数据规模及约定
20%的数据:n<=10。
100%的数据:N<=1000,k,m<=100。Ci<=20000。
题解
线性规划弱化弱化版。弱化到可以用费用流解决。
我们设 n 个布尔变量 A[1], A[2], A[3], ... , A[n],其中 A[i] 表示第 i 个数是否选择(0 表示不选,1 表示选)。
那么得到许多式子(其实是不等式,我们添加辅助变量 B[i],满足所有的 B[i] ≥ 0 就变成等式了):
A[1] + A[2] + ... + A[m] + B[1] = K…………………………………………………… 1
A[2] + A[3] + ... + A[m+1] + B[2] = K……………………………………………… 2
...
A[n-m+1] + A[n-m+2] + ... + A[n] + B[n-m+1] = K………………………… n-m+1
然后用 2 式减去 1 式,3 式减 2 式……n-m+1 式减 n-m 式,得到
A[m+1] + B[2] = A[1] + B[1]
A[m+2] + B[3] = A[2] + B[2]
...
A[n] + B[n-m+1] = A[n-m] + B[n-m]
再添加两个式子(即原来的 1 式和 n-m+1 式):
A[1] + A[2] + ... + A[m] + B[1] = K
K = A[n-m+1] + A[n-m+2] + ... + A[n] + B[n-m+1]
我们会发现所有的 A[i] 和 B[i] 都正好出现在等式的左边和右边各一次。
这样就可以每个等式看成一个节点,左边有一个变量表示流入,右边有一个变量表示流出,左边有常数表示源点向它流入,右边有常数表示它向汇点流出。网络流能够保证所有的等式成立(因为每个节点的流量平衡),然后再加上费用(A[i] 造成的边的费用对应输入第 i 个数),跑最大费用最大流就好了。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
using namespace std;
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 1010
#define maxm 4010
#define oo 2147483647
#define ool (1ll << 60)
#define LL long long
struct Edge {
int from, to, flow; LL cost;
Edge() {}
Edge(int _1, int _2, int _3, LL _4): from(_1), to(_2), flow(_3), cost(_4) {}
};
struct ZKW {
int n, m, s, t, head[maxn], nxt[maxm];
LL cost, ans;
Edge es[maxm];
LL d[maxn];
deque <int> Q;
bool inq[maxn], vis[maxn];
void init() {
m = 0; memset(head, -1, sizeof(head));
return ;
}
void setn(int _) { n = _; return ; }
void AddEdge(int a, int b, int c, LL d) {
es[m] = Edge(a, b, c, d); nxt[m] = head[a]; head[a] = m++;
es[m] = Edge(b, a, 0, -d); nxt[m] = head[b]; head[b] = m++;
return ;
}
bool BFS() {
memset(inq, 0, sizeof(inq));
for(int i = 1; i <= n; i++) d[i] = -ool;
d[t] = 0;
Q.push_front(t); inq[t] = 1;
while(!Q.empty()) {
int u = Q.front(); Q.pop_front(); inq[u] = 0;
for(int i = head[u]; i != -1; i = nxt[i]) {
Edge& e = es[i^1];
if(e.flow && d[e.from] < d[u] + e.cost) {
d[e.from] = d[u] + e.cost;
if(!inq[e.from]) {
if(Q.empty() || d[e.from] >= d[Q.front()]) Q.push_front(e.from);
else Q.push_back(e.from);
inq[e.from] = 1;
}
}
}
}
if(d[s] == -ool) return 0;
for(int i = 0; i < m; i++) es[i].cost += d[es[i].to] - d[es[i].from];
cost += d[s];
return 1;
}
int DFS(int u, int a) {
if(u == t || !a){ ans += cost * a; return a; }
vis[u] = 1;
int flow = 0, f;
for(int i = head[u]; i != -1; i = nxt[i]) {
Edge& e = es[i];
if(!vis[e.to] && e.flow && !e.cost && (f = DFS(e.to, min(a, e.flow)))) {
flow += f; a -= f;
e.flow -= f; es[i^1].flow += f;
if(!a) return flow;
}
}
return flow;
}
int MaxFlow(int _s, int _t) {
s = _s; t = _t;
int flow = 0, f;
while(BFS())
do {
memset(vis, 0, sizeof(vis));
f = DFS(s, oo); flow += f;
} while(f);
return flow;
}
} sol;
int Cnt, val[maxn];
struct Node {
int id;
Node(): id(0) {}
int p() { return id ? id : id = ++Cnt; }
} ns[maxn], Source, Tank;
int main() {
int n = read(), m = read(), k = read();
for(int i = 1; i <= n; i++) val[i] = read();
sol.init();
for(int i = 1; i <= n - m; i++) {
if(i > m) sol.AddEdge(ns[i].p(), ns[i-m].p(), 1, val[i]);
if(i > 1) sol.AddEdge(ns[i].p(), ns[i-1].p(), oo, 0);
}
for(int i = 1; i <= min(m, n - m); i++) sol.AddEdge(ns[i].p(), ns[n-m+1].p(), 1, val[i]);
sol.AddEdge(ns[1].p(), ns[n-m+1].p(), oo, 0);
for(int i = max(n - (m << 1) + 1, 1); i <= n - m; i++) sol.AddEdge(ns[n-m+2].p(), ns[i].p(), 1, val[i+m]);
sol.AddEdge(ns[n-m+2].p(), ns[n-m].p(), oo, 0);
for(int i = n - m + 1; i <= m; i++) sol.AddEdge(ns[n-m+2].p(), ns[n-m+1].p(), 1, val[i]);
sol.AddEdge(Source.p(), ns[n-m+2].p(), k, 0);
sol.AddEdge(ns[n-m+1].p(), Tank.p(), k, 0);
sol.setn(Cnt);
sol.MaxFlow(Source.p(), Tank.p());
printf("%lld\n", sol.ans);
return 0;
}