【题目链接】
HDU 5125 Magic balls [BestCoder Round#20]
【题目描述】
原文是英文就不搬了,快捷版题意:
有$n$个数,每个数有两个属性$a_i$,$b_i$,每次操作可以交换一个数的两个属性,即选取一个$i$然后$swap(a_i, b_i)$,可以进行$k$次操作,问操作后$a_i$形成的最长上升子序列长度。
【解题思路】
LIS问题的简单扩展,我们需要将交换操作也纳入到状态的表示中。
设 $dp0[i][j]$ 表示以 $i$ 结尾,已经用了 $j$ 次操作,并且第 $i$ 个元素并没进行交换的LIS长度。 设 $dp1[i][j]$ 表示以 $i$ 结尾,已经用了 $j$ 次操作,并且第 $i$ 个元素并进行了交换的LIS长度。
转移: $$ dp0[i][j] = max\{\ dp0[t][j] \ (a_t < a_i),\ \ \ \ dp1[t][j]\ (a_t < b_i) \ \} + 1$$
$$ dp1[i][j] = max\{\ dp0[t][j - 1] \ (a_t < b_i),\ \ \ \ dp1[t][j - 1]\ (b_t < b_i) \ \} + 1 $$
答案显然是取所有方案最大值。
然而直接DP是过不了本题的,所以我们需要借助树状数组来加速。
像一般LIS一样,首先离散化,然后只需要建立$k$棵树状数组维护前缀最大值即可。
【AC代码】
。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 2002
#define MAXM 2002
int a[MAXN], b[MAXN];
int n, m, size;
struct TreeArray{
int max[MAXN];
void clear(){
memset(max, 0, sizeof max);
}
int lowbit(signed int x){
return x & -x;
}
int query(int i){
int ans = 0;
while(i){
ans = std::max(ans, max[i]);
i -= lowbit(i);
}
return ans;
}
void set(int i, int x){
while(i <= size){
max[i] = std::max(max[i], x);
i += lowbit(i);
}
}
} arrays[MAXM];
inline int solve(){
int ans = 0;
for(int i = 0; i < n; i++){
for(int j = std::min(i + 1, m); j >= 0; j--){
int x = 0, y = 0;
x = arrays[j].query(a[i] - 1) + 1;
if(j) y = arrays[j - 1].query(b[i] - 1) + 1;
arrays[j].set(a[i], x), arrays[j].set(b[i], y);
ans = std::max(ans, std::max(x, y));
}
}
return ans;
}
int main(){
int t;
scanf("%d", &t);
while(t--){
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) scanf("%d%d", a + i, b + i);
static int aux[MAXN << 1];
memcpy(aux, a, sizeof a), memcpy(aux + n, b, sizeof b);
std::sort(aux, aux + 2 * n);
size = std::unique(aux, aux + 2 * n) - aux;
for(int i = 0; i < n; i++){
a[i] = std::lower_bound(aux, aux + size, a[i]) - aux + 1;
b[i] = std::lower_bound(aux, aux + size, b[i]) - aux + 1;
}
for(int i = 0; i <= m; i++) arrays[i].clear();
printf("%d\\n", solve());
}
return 0;
}
就是这样啦