Vijos1891 学姐的逛街计划 【费用流】*

Vijos1891 学姐的逛街计划


描述

doc 最近太忙了, 每天都有课. 这不怕, doc 可以请假不去上课.
偏偏学校又有规定, 任意连续 n 天中, 不得请假超过 k 天.

doc 很忧伤, 因为他还要陪学姐去逛街呢.

后来, doc发现, 如果自己哪一天智商更高一些, 陪学姐逛街会得到更多的好感度.
现在 doc 决定做一个实验来验证自己的猜想, 他拜托 小岛 预测出了 自己 未来 3n 天中, 每一天的智商.
doc 希望在之后的 3n 天中选出一些日子来陪学姐逛街, 要求在不违反校规的情况下, 陪学姐逛街的日子自己智商的总和最大.

可是, 究竟这个和最大能是多少呢?

格式

输入格式

第一行给出两个整数, n 和 k, 表示我们需要设计之后 3n 天的逛街计划, 且任意连续 n 天中不能请假超过 k 天.
第二行给出 3n 个整数, 依次表示 doc 每一天的智商有多少. 所有数据均为64位无符号整数

输出格式

输出只有一个整数, 表示可以取到的最大智商和.

样例1

样例输入1

5 3
14 21 9 30 11 8 1 20 29 23 17 27 7 8 35

样例输出1

195

限制

对于 20% 的数据, 1 <= n <= 12 , k = 3.
对于 70% 的数据, 1 <= n <= 40 .
对于 100% 的数据, 1 <= n <= 200 , 1 <= k <= 10.


将每个点拆成i和i’,从S向SS连容量为k费用为0的边,SS向每个点i连容量为1费用为0的边,每个i向i’连容量为1费用为价值的边,每个i’向T连容量为1费用为0的边,i’向[i+n,i+n*3]中的所有点连容量为1费用为0的边,跑最大费用流。
                                                                  —by Milky Way


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2010;
const LL INF=1e18;
LL read(){
    LL ans=0,w=1;char c=getchar();
    while(!isdigit(c)&&c!='-')c=getchar();
    if(c=='-')c=getchar(),w=-1;
    while(isdigit(c))ans=ans*10+c-'0',c=getchar();
    return ans*w;
}
struct Edge{
    int from,to;
    LL flow,cap,cost;
}E[N*N>>2];
vector<int> G[N];
int S,SS,T,n,tot=0,p[N];
LL d[N],f[N];
bool inq[N];
void add(int from,int to,LL cap,LL cost){
    E[tot]=(Edge){from,to,0,cap,cost};
    G[from].push_back(tot++);
    E[tot]=(Edge){to,from,0,0,-cost};
    G[to].push_back(tot++);
}
bool SPFA(LL &flow,LL &cost){
    memset(inq,0,sizeof(inq));
    queue<int> q;q.push(S);
    inq[S]=1;
    for(int i=1;i<=T;i++)d[i]=INF;
    d[S]=p[S]=0,f[S]=INF;
    while(!q.empty()){
        int x=q.front();q.pop();inq[x]=0;
        for(int i=0;i<G[x].size();i++){
            Edge e=E[G[x][i]];
            if(e.cap>e.flow&&d[e.to]>d[x]+e.cost){
                d[e .to]=d[x]+e.cost;
                p[e.to]=G[x][i];//记录经过哪条边
                f[e.to]=min(f[x],e.cap-e.flow);
                if(!inq[e.to])q.push(e.to),inq[e.to]=1;
            }
        }
    }
    if(d[T]==INF)return false;
    flow+=f[T],cost+=f[T]*d[T];
    int x=T;
    while(x!=S){
        E[p[x]].flow+=f[T];
        E[p[x]^1].flow-=f[T];
        x=E[p[x]].from;
    }
    return true;
}
LL mincost(){
    LL flow=0,cost=0;
    while(SPFA(flow,cost)){}
    return cost;
}
int main(){
    n=read();int k=read();
    S=0,SS=n*6+1,T=SS+1;
    add(S,SS,k,0);
    for(int i=1;i<=n*3;i++){
        int x=read();
        add(SS,i,1,0);
        add(i,i+n*3,1,-x);
        add(i+n*3,T,1,0);
        for(int j=i+n;j<=n*3;j++)
            add(i+n*3,j,1,0);
    }
    printf("%lld\n",-mincost());
    return 0;
}

转载于:https://www.cnblogs.com/dream-maker-yk/p/9676364.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值