序列选数问题合集

一、对区间中数的个数的限制

1.

bzoj 1283
给出一个长度为n的正整数序列Ci,求一个子序列,使得原序列中任意长度为m的子串中被选出的元素不超过k 个,并且选出的元素之和最大。
n<=1000,k,m<=100,Ci<=20000。

分析

把数列串成一串,从 i i 连向 i + m i+m ,费用 C i C_{i} ,流量 1 1 s s 1 1 ,流量 k k 即可。也即选择一个数之后,其后 k k 个之后便无影响。

2.

loj 6079 山东集训 养猫
限制改为连续 m m 个不超过 k 1 k1 个,不少于 k 2 k2

分析

由于穿过每个截面的流量相同,要求上方有至少 k 2 k2 个就是要求下方至多 k 1 k 2 k1-k2 个,在下方加一个上界即可

3.

ZJOI 2013 防守战线
每个位置的数可以选若干次,限制形如 [ l , r ] [l, r] 中至少选 k k 个,求最小和

分析

两种理解方式:
(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

这样差分之后便使得每个变量出现两次,且系数分别为 ± 1 \pm1
但本题中的性质则略有变化。本题为每一行的非零数连续且相同,如此例:

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

所以我们需要将其旋转 9 0 90^\circ ,也就是对偶。
对偶之后即可变为之前的图,差分即可。

(2)用前缀和转化,方法同CHEFBOOK。
原线性规划形如:
s i s_i 表示 1 i 1-i 选了几个数
最小化 :
( s i s i 1 ) c i \sum(s_i-s_{i-1})*c_i
满足:
s r s l 1 &gt; = d k s_r-s_{l-1} &gt;= d_k
s i s i 1 &gt; = 0 s_i-s_i-1 &gt;= 0
s i &gt; = 0 s_i &gt;= 0
注意到每个限制都只有两个变量,且参数分别为 ± 1 \pm1 。如下例:

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

则将其对偶之后(行列交换),每个变量恰出现两次,且系数分别为 ± 1 \pm1
费用流即可

4.

在3的基础上,增加一种限制形如[l, r]中至多选k个,求最小/最大和

5. 终极版本

在4的基础上,增加一种限制,每个位置有一个至多选的次数,求最大/最小和
完整描述如下:
有一个数列,在这个数列中选一些数。位置 i i 的数可以被选择 t i t_i 次。现在有若干限制,形如在区间 [ l , r ] [l, r] 中至少/至多选出几个数,求最小/最大和。

二、对区间中选出数和的限制

1.

一个数列,限制形如区间 [ l , r ] [l, r] 的和至少为$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;
}

猜你喜欢

转载自blog.csdn.net/hychxb/article/details/82695437