学姐的逛街计划(费用流)

学姐的逛街计划

描述

doc d o c 最近太忙了, 每天都有课. 这不怕, doc d o c 可以请假不去上课.
偏偏学校又有规定, 任意连续 n n 天中, 不得请假超过 k 天.
doc d o c 很忧伤, 因为他还要陪学姐去逛街呢.
后来, doc d o c 发现, 如果自己哪一天智商更高一些, 陪学姐逛街会得到更多的好感度.
现在 doc d o c 决定做一个实验来验证自己的猜想, 他拜托 小岛 预测出了 自己 未来 3n 3 n 天中, 每一天的智商.
doc d o c 希望在之后的 3n 3 n 天中选出一些日子来陪学姐逛街, 要求在不违反校规的情况下, 陪学姐逛街的日子自己智商的总和最大.
可是, 究竟这个和最大能是多少呢?

输入格式

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

输出格式

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

样例输入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. 1 <= n <= 12 , k = 3.
对于 70% 的数据, 1<=n<=40. 1 <= n <= 40 .
对于 100% 的数据, 1<=n<=200,1<=k<=10. 1 <= n <= 200 , 1 <= k <= 10.















解:

网络流的一个套路:用流量平衡表达等式。
di取值0或1表示第 i i 天是否逛街。
建立不等式:
d1+d2+d3+...+dnk

di+di+1+di+2+...+di+n1k d i + d i + 1 + d i + 2 + . . . + d i + n − 1 ≤ k

d2n+1+d2n+2+d2n+3+...+d3nk d 2 n + 1 + d 2 n + 2 + d 2 n + 3 + . . . + d 3 n ≤ k
增加调节变量( yi y i ):
d1+d2+d3+...+dn+y1=k d 1 + d 2 + d 3 + . . . + d n + y 1 = k

di+di+1+di+2+...+di+n1+yi=k d i + d i + 1 + d i + 2 + . . . + d i + n − 1 + y i = k

d2n+1+d2n+2+d2n+3+...+d3n+y2n+1=k d 2 n + 1 + d 2 n + 2 + d 2 n + 3 + . . . + d 3 n + y 2 n + 1 = k

用上式减下式:
d1+y1=dn+1+y2 d 1 + y 1 = d n + 1 + y 2

di+yi=dn+i+yi+1 d i + y i = d n + i + y i + 1

d2n+y2n=d3n+y2n+1 d 2 n + y 2 n = d 3 n + y 2 n + 1
由于有变量不足,我们增加两个等式:
k=d1+d2+d3+...+dn+y1 k = d 1 + d 2 + d 3 + . . . + d n + y 1
d2n+1+d2n+2+d2n+3+...+d3n+y2n+1=k d 2 n + 1 + d 2 n + 2 + d 2 n + 3 + . . . + d 3 n + y 2 n + 1 = k

这样的话,每个变量出现两次,左右各一次。
我们把每个等式看成点,每个变量看作边,在左边连入流,右边连出流。最后两个变量连源点和汇点,流量为 k k
跑费用流即可。
想一下有下界怎么做???
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct lxy{
    int next,to,flow,cost;
}b[100005];

int head[1005],data[1005],cnt=-1;
int n,k,s,t,ans;
int dis[1005];
bool vis[1005];
queue <int> d;

void add(int op,int ed,int len,int w){
    b[++cnt].next=head[op];
    b[cnt].to=ed;
    b[cnt].flow=len;
    b[cnt].cost=w;
    head[op]=cnt;
}

bool bfs(){
    memset(dis,0x7f,sizeof(dis));
    memset(vis,0,sizeof(vis)); 
    dis[s]=0;d.push(s);
    while(!d.empty()){
        int now=d.front();d.pop();vis[now]=0;//cout<<now<<endl;
        for(int i=head[now];i!=-1;i=b[i].next)
          if(b[i].flow!=0&&dis[now]+b[i].cost<dis[b[i].to]){
            dis[b[i].to]=dis[now]+b[i].cost;
            if(vis[b[i].to]==0) d.push(b[i].to),vis[b[i].to]=1;
          }
    }
    if(dis[t]==0x7f7f7f7f) return false;
    return true;
}

int dfs(int u,int a){
    if(u==t||a==0) return a;
    vis[u]=1;
    int f,flow=0;
    for(int i=head[u];i!=-1;i=b[i].next)
      if(vis[b[i].to]==0&&b[i].flow!=0&&dis[b[i].to]==dis[u]+b[i].cost){
        f=dfs(b[i].to,min(b[i].flow,a));
        b[i].flow-=f;b[i^1].flow+=f;
        flow+=f;a-=f;
        ans+=f*b[i].cost;
        if(a==0) break;
      }
    return flow;
}

void dinic(){
    while(bfs())
      dfs(s,0x7f7f7f7f);
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&k);s=0,t=1001;
    for(int i=1;i<=3*n;i++) scanf("%d",&data[i]);
    for(int i=1;i<2*n;i++) add(i,i+1,k,0),add(i+1,i,0,0);
    add(2*n+1,1,k,0),add(1,2*n+1,0,0);
    add(2*n,2*n+2,k,0),add(2*n+2,2*n,0,0);
    for(int i=1;i<=n;i++) add(i,i+n,1,-data[i+n]),add(i+n,i,0,data[i+n]);
    for(int i=1;i<=n;i++) add(2*n+1,i,1,-data[i]),add(i,2*n+1,0,data[i]);
    for(int i=1;i<=n;i++) add(i+n,2*n+2,1,-data[2*n+i]),add(2*n+2,i+n,0,data[2*n+i]);
    add(s,2*n+1,k,0),add(2*n+1,s,0,0);add(2*n+2,t,k,0),add(t,2*n+2,0,0);
    dinic();
    printf("%d",-ans);
}

有下界限制一下y的取值即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值