BZOJ 1283: 序列 (最大费用流)

题意

有n个正整数,要选取里面的一些数,在保证每m个连续的数中最多选k个的情况下,使得得到的值最大.

分析

我们可以把问题先转化为选k次,每一次每m个数只能选一个.那么根据贪心的策略,每m个里一定会选一个.那么先建一个源点S,一个汇点T,连边就先用容量为k,费用为0的边把S,数组,T顺次连起来,然后每一个位置i向i+m(如果>n就连向T)连一条容量为1,费用为a[i]的边.做最大费用流即可.

CODE

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 1005;
const int MAXM = 5005;
const int inf = 1e9;
int n, m, k, S, T, fir[MAXN], info[MAXN], cnt;
struct edge { int to, nxt, c, w; }e[MAXM];
inline void add(int u, int v, int cc, int ww) {
	e[cnt] = (edge) { v, fir[u], cc, ww }, fir[u] = cnt++;
	e[cnt] = (edge) { u, fir[v], 0, -ww }, fir[v] = cnt++;
}
int dis[MAXN], Ans; deque<int>q;
bool inq[MAXN], vis[MAXN];
inline bool spfa() {
	memset(dis, 0x3f, sizeof dis);
	q.push_back(T); dis[T] = 0;
	while(!q.empty()) {
		int u = q.front(), v; q.pop_front(); inq[u] = 0;
		for(int i = fir[u]; ~i; i = e[i].nxt)
			if(e[i^1].c && dis[v=e[i].to] > dis[u] + e[i^1].w) {
				dis[v] = dis[u] + e[i^1].w;
				if(!inq[v]) {
					inq[v] = 1;
					if(!q.empty() && dis[v] < dis[q.front()]) q.push_front(v);
					else q.push_back(v);
				}
			}
	}
	return dis[S] < inf;
}
int aug(int u, int Max) {
	if(u == T) { Ans += Max * dis[S]; return Max; }
	int delta, flow = 0, v;
	vis[u] = 1;
	for(int &i = info[u]; ~i; i = e[i].nxt)
		if(e[i].c && dis[v=e[i].to] + e[i].w == dis[u] && !vis[v]) {
			delta = aug(v, min(Max-flow, e[i].c));
			e[i].c -= delta, e[i^1].c += delta;
			if((flow+=delta) == Max) break;
		}
	vis[u] = 0;
	return flow;
}
inline int dinic() {
	Ans = 0;
	while(spfa())
		memcpy(info, fir, (T+1)<<2), aug(S, inf);
	return Ans;
}
int main () {
	memset(fir, -1, sizeof fir);
	scanf("%d%d%d", &n, &m, &k);
	S = 0, T = n+1;
	for(int i = 1, x; i <= n; ++i) {
		scanf("%d", &x);
		add(i-1, i, k, 0);
		add(i, min(i+m, n+1), 1, -x);
	}
	add(n, n+1, k, 0);
	printf("%d\n", -dinic());
}
posted @ 2019-12-14 14:51  _Ark  阅读(69)  评论(0编辑  收藏  举报