题意:给定一个长度为n的序列,要求选一些数,使得任意一个长度为m个区间中最多选k个数,求最大的和
解法: 论文上的题目 《浅析信息学中的“分”与“合”》
最大费用最大流
把这个序列用流量为k费用为0的边连成一条直线 然后第i个点向第i+m个点连一条费用为a[i]流量为1的边
跑最大费用最大流即可
反正具体看论文吧,注意论文是拆了点的,但是这个模型拆和不拆是等效的。
//BZOJ 1283 最大费用最大流
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
const int maxm = 100010;
const int inf = 0x3f3f3f3f;
struct node{
int st, en, flow, cost, next;
node(){}
node(int st, int en, int flow, int cost, int next) : st(st), en(en), flow(flow), cost(cost), next(next){}
}E[maxm];
int p[maxn], num;
void init(){
memset(p, -1, sizeof(p));
num = 0;
}
void add(int st, int en, int flow, int cost){
E[num] = node(st,en,flow,cost,p[st]);
p[st]=num++;
E[num] = node(en,st,0,-cost,p[en]);
p[en]=num++;
}
int pre[maxn], dis[maxn];
bool fg[maxn];
bool spfa(int st, int en)
{
for(int i=0; i<=en; i++)
fg[i]=0, dis[i]=-1, pre[i]=-1;
queue<int>q;
q.push(st);
fg[st]=1;
dis[st]=1;
while(!q.empty()){
int u = q.front(); q.pop();
fg[u]=0;
for(int i=p[u];~i;i=E[i].next){
int v = E[i].en;
if(E[i].flow&&dis[v]<dis[u]+E[i].cost){
dis[v] = dis[u]+E[i].cost;
pre[v] = i;
if(!fg[v]){
fg[v]=1;
q.push(v);
}
}
}
}
return dis[en] != -1;
}
int solve(int st, int en){
int ans = 0;
while(spfa(st,en))
{
int d = inf;
for(int i = pre[en]; ~i; i = pre[E[i].st]) d = min(d, E[i].flow);
for(int i = pre[en]; ~i; i = pre[E[i].st]){
ans += d * E[i].cost;
E[i].flow -= d;
E[i^1].flow += d;
}
}
return ans;
}
int a[maxn];
int main()
{
int n, m, k;
scanf("%d%d%d", &n,&m,&k);
init();
int st, en;
st = 0, en = n+1;
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);
add(i-1,i,k,0);
if(i+m<=n) add(i,i+m,1,a[i]);
else add(i,en,1,a[i]);
}
int ans = solve(st, en);
printf("%d\n", ans);
return 0;
}