[BZOJ1283]序列
[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; }