[BZOJ1283]序列

[BZOJ1283]序列

试题描述

给出一个长度为n的正整数序列Ci,求一个子序列,使得原序列中任意长度为m的子串中被选出的元素不超过K(K,M<=100) 个,并且选出的元素之和最大。

输入

第1行三个数N,m,k。 接下来N行,每行一个字符串表示Ci。

输出

最大和。

输入示例

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;
}

 

posted @ 2017-04-18 16:30  xjr01  阅读(235)  评论(0编辑  收藏  举报