题目描述
何老师沉迷偶像大师无法自拔,现在他拥有n张偶像卡,每张卡有一个能力值,为了准备下一场对战,需要从拥有的偶像卡中选出一些使得它们的能力值之和最大,并且在任意连续的k张卡中,至少要选出a张卡,但不能选超过b张卡。
解法
先把条件按照线性规划的形式写出来:
a≤∑ki=1xi≤b
a≤∑k+1i=2xi≤b
a≤∑k+2i=3xi≤b
……
a≤∑ni=n−k+1xi≤b
使得
max∑ni=1xici
将限制的每条式子补上一个变量使得不等式变成等式
y1+sumki=1xi=b
其中
y1<=b−a
添加第0条式子和第n+1条式子0=0
对相邻的两项做差
第0条式子:
y1+∑ki=1xi=b
第1到n-k+1条式子:
yi+1−yi+xi+k−xi=0
第n-k+2条式子:
−yn−∑ni=n−k+1xi=−b
每个变量在式子里面都出现了两次,一次正一次负
把每个变量看成一条边,系数为1表示出边,系数为-1表示入边
xi
的费用为
ci
,
yi
的费用为
0
<script type="math/tex" id="MathJax-Element-140">0</script>
多余的常数项是从源点/汇点流出来的流量
建图跑最大费用最大流
#include <bits/stdc++.h>
#define INF (1<<29)
#define N 605
#define M 200050
using namespace std;
typedef long long LL;
int head[N],cnt=1,tot,S,T;
int p[N],L[N],a,b,c[N],d;
LL dis[N];
int n,m,k;
LL ans;
inline int rd() {
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;
}
struct Edge{ int a,b,v,cost,next; }e[M],id;
inline void add(int a,int b,int v,int cost) {
if (!a || !b) return ;
e[++cnt] = (Edge){ a,b,v,cost,head[a] }, head[a] = cnt;
e[++cnt] = (Edge){ b,a,0,-cost,head[b] },head[b] = cnt;
}
#define cp e[i].v
#define B e[i].b
bool SPFA() {
bool flag = false;
for (int i=1;i<=tot;i++) p[i] = 0, dis[i] = -(1LL<<62);
dis[S] = 0;
queue<int> q; q.push(S);
while (!q.empty()) {
int u = q.front(); q.pop();
if (u == T) flag = true;
for (int i=head[u];i;i=e[i].next)
if (cp > 0 && dis[u] + e[i].cost > dis[B]) {
dis[B] = dis[u] + e[i].cost;
p[B] = i;
q.push(B);
}
}
return flag;
}
void mcf() {
int g = p[T] , flow = INF;
while (g) {
flow = min(flow , e[g].v);
g = p[ e[g].a ];
}
g = p[T];
while (g) {
e[g ].v -= flow;
e[g^1].v += flow;
ans += 1LL * e[g].cost * flow;
g = p[ e[g].a ];
}
}
void solve() {
for (int i=0;i<=tot;i++) head[i] = 0;
for (int i=0;i<=cnt;i++) e[i] = id;
cnt = 1, tot = 0;
n = rd(), k = rd(), a = rd(), b = rd(), d = n-k+1;
for (int _=1;_<=n;_++) c[_] = rd();
S = ++tot, T = ++tot;
for (int _=0;_<=n;_++) L[_] = ++tot;
add(S, L[0], b, 0), add(L[d], T, b, 0);
//x[i]
for (int i=1;i<=n;i++) {
int p1 = max(i-k, 0), p2 = min(i, d);
add(L[p1], L[p2], 1, c[i]);
}
//y[i]
for (int i=0;i<d;i++) add(L[i], L[i+1], b-a, 0);
ans = 0;
while (SPFA()) mcf();
printf("%lld\n",ans);
}
int main() {
for (int T=rd();T;T--) solve();
return 0;
}