代码源 $C$ 队第 $87$ 周总结
闲话
总结
| 日期 | 星期 | \(87\) 周作业 | abc479 |
|---|---|---|---|
| \(2026.10.5\) | 一 | \(A,B,C,D,E\) | |
| \(2026.10.4\) | 四 | \(F\) | |
| \(2025.10.5\) | 五 | \(G,H\) |
感受
题目背景
这周周作业过水,速通!
\(I\) 题想出来了,WA 了好几发,好像做法稍微有些假,但和正解无限接近。恶心的真的不想做了。\(J\) 一开始的加一环节真没想出来,似乎乱搞一下就过了。
题目描述
我 abc 做出了 \(\frac{x}{2}\) 题。因此在 \(m\) 个时刻可以用 \((a_i, b_i)\) 来形容我对 abc 的感受。
由于防止我在 \(m\) 个时刻之后精神状态失常,我必须在打 abc 之前提前更改我的想法!
已知我的总平衡值 \(c = \displaystyle{\sum_{i=1}^m |a_i-b_i|}\),现在我可以提前把这 \(2n\) 个数交换,以求总平衡值不超过 \(k\)。
但我的精神状态已经不平衡了,因此制定的方案容易让自己陷入疯狂之中。请善良的你帮我求出符合要求的最小操作次数。
输入格式
第一行输入两个非负整数 \(m, k\)。
接下来 \(m\) 行,第 \(i+1(1 \le i \le m)\) 行输入两个数 \(a_i, b_i\)。
然后就完了。
输出格式
输出一个整数,表示答案。
输入输出样例
3 0
3 3
2 5
2 5
1
说明/提示
数据规模与约定
对于 \(100 \%\) 的数据,\(1 \le n \le 16,1 \le k, a_i, b_i \le 10^6\)。
题解
本题是在 \(2026.1\) 的模拟赛中场切的题。
我们发现每个 \((a_i, b_i)\) 至多只会被交换一次。
根据数据范围 \(1 \le n \le 16\),可以想到复杂度为 \(O(3^n)\) 的状压 DP。
我们设 \(g_i\) 表示状态压缩数值 \(i\) 的状态下,是否能完成合法交换。
把 \(i\) 的第 \(j\) 位为 \(1\) 的 \((a_j, b_j)\) 都放到一个数组中,排一下序,判断相邻两个之差是否小于等于 \(k\)。若是则说明合法。
设 \(f_i\) 表示状态压缩数值 \(i\) 的状态下,完成合法交换的最小操作次数。
我们称一次交换为“满的”,当这次交换把每个 \((a_i, b_i)\) 都交换了一次,\(g_i\) 表示一次“满的”操作。
则 \(f_i = \min_{j = i 的子集} g_j + f_{i - j}\)。
这题就做完了。
话说今天晚上才打 abc,我怎么知道自己的想法呢?
代码
# include <cmath>
# include <cstdio>
# include <cstring>
# include <algorithm>
# define min(x, y) (x < y? x: y)
int n, m, tot, flag, b[100], f[100000], g[100000];
struct Node {
int x, y;
} a[100];
bool cmp(Node x, Node y) {
return x.x < y.x;
}
int main( ) {
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%d %d", &a[i].x, &a[i].y);
memset(g, 0x3f, sizeof(g));
for(int i = 1; i < (1 << n); i++) {
tot = 0, flag = 1;
for(int j = 1; j <= n; j++)
if(i & (1 << (j - 1))) {
b[++ tot] = a[j].x;
b[++ tot] = a[j].y;
}
std::sort(b + 1, b + tot + 1);
for(int j = 1; j <= tot; j += 2)
if(abs(b[j] - b[j + 1]) > m)
flag = 0;
if(flag == 1)
g[i] = tot / 2 - 1;
}
for(int i = 0; i < (1 << n); i++) {
f[i] = g[i];
for(int j = i; j > 0; j = ((j - 1) & i))
f[i] = min(f[i], f[i - j] + g[j]);
}
if(f[(1 << n) - 1] > 1e9)
printf("%d\n", -1);
else
printf("%d\n", f[(1 << n) - 1]);
return 0;
}

浙公网安备 33010602011771号