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

题意:给定一个长度为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值