bzoj1283 序列

该博客介绍了一种解决序列问题的方法,要求在不超过K个元素的情况下找到一个子序列,使得其和最大。这是一个线性规划问题,可以通过建立松弛型线性规划模型并转化为网络流问题来求解。博主探讨了如何构建矩阵和添加限制条件,并指出在特定情况下,多路增广路径在网络流中的效率并不优于单路增广。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.elijahqi.win/archives/2903
Description

给出一个长度为 的正整数序列Ci,求一个子序列,使得原序列中任意长度为 的子串中被选出的元素不超过K(K,M<=100) 个,并且选出的元素之和最大。
Input

第1行三个数N,m,k。 接下来N行,每行一个字符串表示Ci。
Output

最大和。
Sample Input

10 5 3
4 4 4 6 6 6 6 6 4 4
Sample Output

30
HINT

20%的数据:n<=10。
100%的数据:N<=1000,k,m<=100。Ci<=20000。

Source

By YM

考虑这题直接单纯形怎么做 直接列出式子即可 注意在矩阵的最后得添加一些限制使得x<=1

建图类似bzoj 志愿者招募
首先写出松弛型线性规划的这个式子

0xi1yi0x1+x2+...+xm+y1=kx2+x3+...+xm+1+y2=k...xnm+1+xnm+2+...+xn+ynm+1=k 0 ≤ x i ≤ 1 y i ≥ 0 x 1 + x 2 + . . . + x m + y 1 = k x 2 + x 3 + . . . + x m + 1 + y 2 = k . . . x n − m + 1 + x n − m + 2 + . . . + x n + y n − m + 1 = k
然后在第一行之前添加0=0最后一行之后添加0=0 然后每行用i-(i-1)即可得到一个新的关系
x1+x2+...+xm+y1k=0xm+1x1+y2y1=0xm+2x2+y3y2=0...xn1xnm1+ynmynm1=0xnxnm+ynm+1ynm=0xnm+1xnm+2...xnynm+1+k=0 x 1 + x 2 + . . . + x m + y 1 − k = 0 x m + 1 − x 1 + y 2 − y 1 = 0 x m + 2 − x 2 + y 3 − y 2 = 0 . . . x n − 1 − x n − m − 1 + y n − m − y n − m − 1 = 0 x n − x n − m + y n − m + 1 − y n − m = 0 − x n − m + 1 − x n − m + 2 − . . . − x n − y n − m + 1 + k = 0

然后一个等式看作一个点 我们假设负数的为流出的正数为流入 同时一对正负恰好有对应关系
那么就针对x的这个限制做流量为1 然后费用为给定的边 然后同理y的这个边就做流量为inf 费用为0 最后我的1式 还有最后的那个式子分别连接下源点和汇点流量限制成k即可

因为网络流的类型太特殊了 多路增广并没有跑的比单路快..

#include<queue>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define N 1100
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
struct node{
    int y,next,c,z;
}data[30000];
int num=1,h[N],cur[N],dis[N],T,n,m,k,ans,pre[N],path[N],in[N];bool flag[N];
inline void insert1(int x,int y,int z,int c){
    //printf("%d %d %d %d\n",x,y,z,c);
    data[++num].y=y;data[num].next=h[x];data[num].z=z;data[num].c=c;h[x]=num;
    data[++num].y=x;data[num].next=h[y];data[num].z=0;data[num].c=-c;h[y]=num;
}
inline bool spfa(){
    queue<int>q;q.push(0);for (int i=1;i<=T;++i) dis[i]=-inf;
    memset(pre,-1,sizeof(pre)); 
    memset(flag,0,sizeof(flag));flag[0]=0;dis[0]=0;
    while(!q.empty()){
        int x=q.front();q.pop();flag[x]=0;in[x]=0;
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,c=data[i].c,z=data[i].z;
            if (dis[x]+c>dis[y]&&z){
                pre[y]=x;path[y]=i;
                dis[y]=dis[x]+c;if (!flag[y]) q.push(y),flag[y]=1;
            } 
        }
    }return dis[T]!=-inf;
}
inline int dfs(int x,int s){
    if (x==T) {ans+=dis[T]*s;return s;}int ss=s;in[x]=1;
    for (int &i=cur[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z,c=data[i].c;
        if (dis[x]+c==dis[y]&&z&&!in[y]){
            int xx=dfs(y,min(z,s));if (!xx) dis[y]=-inf;
            s-=xx;data[i].z-=xx;data[i^1].z+=xx;if(!s) return ss;
        }
    }return ss-s;
}
int main(){
    freopen("bzoj1283.in","r",stdin);
    n=read();m=read();k=read();T=n-m+3;
    for (int i=1;i<=m;++i) insert1(i+1,1,1,read());
    for (int i=m+1;i<=n-m;++i) insert1(i+1,i-m+1,1,read());
    for (int i=n-m+1;i<=n;++i) insert1(T-1,i-m+1,1,read());
    for (int i=2;i<=T-1;++i) insert1(i,i-1,inf,0);
    insert1(1,T,k,0);insert1(0,T-1,k,0);
/*  while(spfa()){
        int minn=inf,now=T;
        while(now) minn=min(minn,data[path[now]].z),now=pre[now];ans+=minn*dis[T];now=T;
        while(now) data[path[now]].z-=minn,data[path[now]^1].z+=minn,now=pre[now];
    }*/
    while(spfa()) memcpy(cur,h,sizeof(h)),dfs(0,inf);
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值