有3N个数,你需要选出一些数,首先保证任意长度为N的区间中选出的数的个数<=K个,其次要保证选出的数的个数最大。
有3N个数,你需要选出一些数,首先保证任意长度为N的区间中选出的数的个数<=K个,其次要保证选出的数的个数最大。
第一行两个整数N,K。
第二行有3N个整数。
一行一个整数表示答案。
【数据范围】
N<=200,K<=10。
By Sbullet
费用流+线性规划。
这道题和bzoj1061志愿者招募的做法很类似。先列出流量的平衡方程(小于等于可以用加入辅助变量解决),再通过差分建立2n+2个等式。将这2n+2个等式看作点,根据流量平衡方程来连边,最后跑最大费用最大流。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define maxn 1000 #define maxm 20000 #define ll long long #define pa pair<int,int> #define inf 1000000000 using namespace std; struct edge_type { int from,to,next,v,c; }e[maxm]; int n,k,s,t,ans=0,cnt=1; int head[maxn],dis[maxn],p[maxn],a[maxn]; bool inq[maxn]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void add_edge(int x,int y,int v,int c) { e[++cnt]=(edge_type){x,y,head[x],v,c};head[x]=cnt; e[++cnt]=(edge_type){y,x,head[y],0,-c};head[y]=cnt; } inline bool spfa() { queue<int>q; memset(inq,false,sizeof(inq)); F(i,1,t) dis[i]=inf; dis[s]=0;inq[s]=true;q.push(s); while (!q.empty()) { int x=q.front();inq[x]=false;q.pop(); for(int i=head[x];i;i=e[i].next) { int y=e[i].to; if (e[i].v&&dis[y]>dis[x]+e[i].c) { dis[y]=dis[x]+e[i].c; p[y]=i; if (!inq[y]){inq[y]=true;q.push(y);} } } } return dis[t]!=inf; } inline void mcf() { while (spfa()) { int tmp=inf; for(int i=p[t];i;i=p[e[i].from]) tmp=min(tmp,e[i].v); ans+=dis[t]*tmp; for(int i=p[t];i;i=p[e[i].from]){e[i].v-=tmp;e[i^1].v+=tmp;} } } int main() { n=read();k=read(); s=2*n+3;t=s+1; F(i,1,3*n) a[i]=read(); F(i,1,n) { add_edge(1,i+1,1,-a[i]); add_edge(i+1,i+n+1,1,-a[n+i]); add_edge(i+n+1,2*n+2,1,-a[2*n+i]); } F(i,1,2*n+1) add_edge(i,i+1,inf,0); add_edge(s,1,k,0); add_edge(2*n+2,t,k,0); mcf(); printf("%d\n",-ans); }