一、对区间中数的个数的限制
1.
bzoj 1283
给出一个长度为n的正整数序列Ci,求一个子序列,使得原序列中任意长度为m的子串中被选出的元素不超过k 个,并且选出的元素之和最大。
n<=1000,k,m<=100,Ci<=20000。
分析
把数列串成一串,从 连向 ,费用 ,流量 。 连 ,流量 即可。也即选择一个数之后,其后 个之后便无影响。
2.
loj 6079 山东集训 养猫
限制改为连续
个不超过
个,不少于
个
分析
由于穿过每个截面的流量相同,要求上方有至少 个就是要求下方至多 个,在下方加一个上界即可
3.
ZJOI 2013 防守战线
每个位置的数可以选若干次,限制形如
中至少选
个,求最小和
分析
两种理解方式:
(1)用差分的方法使得每个变量仅在两个式子中出现,且系数一正一负。
这是一种经典的做法,从志愿者招募一题开始出现。若是使用这种做法,需要系数矩阵每一列不为0的数连续且相等。如此例:
a1 | a2 | a3 | a4 | a5 | a6 |
---|---|---|---|---|---|
0 | 1 | 1 | 0 | 0 | 1 |
0 | 1 | 1 | 1 | 0 | 1 |
1 | 0 | 1 | 1 | 1 | 1 |
1 | 0 | 0 | 0 | 1 | 0 |
这样差分之后便使得每个变量出现两次,且系数分别为
。
但本题中的性质则略有变化。本题为每一行的非零数连续且相同,如此例:
a1 | a2 | a3 | a4 | a5 | a6 |
---|---|---|---|---|---|
0 | 1 | 1 | 1 | 0 | 0 |
1 | 1 | 1 | 1 | 0 | 0 |
0 | 0 | 0 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 | 0 |
所以我们需要将其旋转
,也就是对偶。
对偶之后即可变为之前的图,差分即可。
(2)用前缀和转化,方法同CHEFBOOK。
原线性规划形如:
设
表示
选了几个数
最小化 :
满足:
注意到每个限制都只有两个变量,且参数分别为
。如下例:
a1 | a2 | a3 | a4 | a5 | a6 |
---|---|---|---|---|---|
0 | -1 | 0 | 1 | 0 | 0 |
0 | 0 | -1 | 1 | 0 | 0 |
-1 | 0 | 0 | 0 | 0 | 1 |
-1 | 0 | 1 | 0 | 0 | 0 |
则将其对偶之后(行列交换),每个变量恰出现两次,且系数分别为
费用流即可
4.
在3的基础上,增加一种限制形如[l, r]中至多选k个,求最小/最大和
5. 终极版本
在4的基础上,增加一种限制,每个位置有一个至多选的次数,求最大/最小和
完整描述如下:
有一个数列,在这个数列中选一些数。位置
的数可以被选择
次。现在有若干限制,形如在区间
中至少/至多选出几个数,求最小/最大和。
二、对区间中选出数和的限制
1.
一个数列,限制形如区间 的和至少为$d $,求最少选出几个数。
代码
1-1
//T1, bzoj1283
#include <iostream>
#include <cstdio>
#include <cstring>
#include <bits/stdc++.h>
#include <queue>
#include <set>
#define MAXN 1050
#define il inline
#define pb push_back
#define ri register int
#define MOD
#define INF (1<<28)
using namespace std;
typedef long long LL;
typedef long double LD;
struct node {
int v, c, w;
node *next, *rev;
}pool[MAXN<<4], *h[MAXN<<4], *pre[MAXN<<4];
int n, m, k, cnt, w[MAXN], s, t, MAXF, MINC, inq[MAXN<<4], val[MAXN<<4], c[MAXN<<4], q[MAXN<<5];
il void adde(int u, int v, int c, int w) {
node *p = &pool[cnt++], *q = &pool[cnt++];
*p = node {v, c, w, h[u], q}, h[u] = p;
*q = node {u, 0, -w, h[v], p}, h[v] = q;
}
bool SPFA() {
int front = 0, rear = 1;
for(ri i = s; i <= t; ++i) c[i] = inq[i] = 0, val[i] = -2e9;
q[0] = s, val[s] = 0, c[s] = INF;
while(front < rear) {
int u = q[front++];
inq[u]--;
for(node *p = h[u]; p; p = p->next) {
if(p->c && val[p->v] < val[u] + p->w) {
pre[p->v] = p;
c[p->v] = min(p->c, c[u]);
val[p->v] = val[u] + p->w;
if(!inq[p->v]) inq[p->v]++, q[rear++] = p->v;
}
}
}
if(val[t] <= -2e9) return 0;
MAXF += val[t], MINC += val[t]*c[t];
int u = t;
while(u != s) {
pre[u]->c -= c[t];
pre[u]->rev->c += c[t];
u = pre[u]->rev->v;
}
return 1;
}
int main() {
scanf("%d%d%d", &n, &m, &k), t = n+1;
for(ri i = 1; i <= n; ++i) scanf("%d", &w[i]);
adde(s, 1, k, 0);
for(ri i = 1; i <= n; ++i) adde(i, min(i+m, n+1), 1, w[i]);
for(ri i = 1; i <= n; ++i) adde(i, i+1, INF, 0);
while(SPFA());
printf("%d", MINC);
return 0;
}
###1-2
//T2 养猫
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
#include <math.h>
#include <set>
#define INF (1<<29)
#define LLINF (1LL<<60)
#define MAXN 5050
using namespace std;
/*
第一行四个整数 n,k,ms,me
第二行包含 n n n 个整数,代表 si
第三行包含 n 个整数,代表 ei。*/
int n, k, ms, me, cnt, sl[MAXN], ea[MAXN], SS, S, T, maxn, minn, FLOW;
int q[MAXN<<2], c[MAXN<<2], inq[MAXN<<2], go[MAXN<<2];
long long sum, w[MAXN<<2], FEE;
struct node{
int v, c, w, ty;
node *rev, *next;
}pool[MAXN<<4], *h[MAXN<<2], *pre[MAXN<<2];
inline void addedge(int u, int v, int c, int w, int ty) {
node *p1 = &pool[cnt++], *p2 = &pool[cnt++];
*p1 = node{v, c, w, ty, p2, h[u]}, h[u] = p1;
*p2 = node{u, 0, -w, -ty, p1, h[v]}, h[v] = p2;
}
bool SPFA() {
int front = 0, rear = 1;
memset(w, 128, sizeof(w)), memset(inq, 0, sizeof(inq)), memset(c, 0, sizeof(c));
int IISS = w[0];
q[0] = SS, w[SS] = 0, inq[SS] = 1, c[SS] = INF;
while(front < rear ) {
int u = q[front++];
inq[u]--;
for(node *p = h[u]; p; p = p->next) {
if(p->c && w[p->v] < w[u] + p->w) {
w[p->v] = w[u] + p->w;
c[p->v] = min(c[u], p->c);
pre[p->v] = p;
if(!inq[p->v]) q[rear++] = p->v, inq[p->v]++;
}
}
}
if(w[T] <= IISS) return 0;
FLOW += c[T], FEE += w[T]*c[T];
int u = T;
while(u != SS) {
node *p = pre[u];
if(p->ty == 1) go[p->rev->v]++;
if(p->ty == -1) go[p->v]--;
//cout<<u<<' '<<p->v<<' '<<go[u]<<' '<<go[p->v]<<endl;
p->c -= c[T];
p->rev->c += c[T];
u = p->rev->v;
}
return 1;
}
int main(){
scanf("%d%d%d%d", &n, &k, &ms, &me);
//pre 全是睡觉 上面的流即吃饭
// max = k-ms, min = k-me
for(int i = 1; i <= n; i++) scanf("%d", &sl[i]), sum += sl[i];
for(int i = 1; i <= n; i++) scanf("%d", &ea[i]);
SS = 0, S = n+2, T = n+1;
maxn = k-ms, minn = me;
addedge(SS, S, maxn, 0, 0);
for(int i = 1; i <= k; i++) {
addedge(S, i, 1, 0, 0);
}
for(int i = 1; i <= n; i++) {
addedge(i, i+1, maxn-minn, 0, 0);
addedge(i, min(n+1, i+k), 1, ea[i]-sl[i], 1);
}
while(SPFA());
printf("%lld\n", sum+FEE);
for(int i = 1; i <= n; i++)
if(go[i]) printf("E");
else printf("S");
return 0;
}