BZOJ1237: [SCOI2008]配对
感觉此题还是挺可做的...
首先考虑最无脑的做法:
我需要知道接下来要给谁配对,在另一个数组中还没有被选的有哪些,
并记录已选的造成的贡献
要想知道没选的有哪些的话,这直接记录问题就很大了,考虑能不能省去这一步
那么无非是两个数组被选的集合的右端点同时往右扩张,
或是每个数可与其配对的数的范围大概为一个常数
直觉是这样的,考虑证明一下(其实我也不会证...)
先把两个数组排序,
这样对于一个 ai 来说他可选的一定是在 b 数组中的一段连续区间
在注意到每个数组中元素不重复之后,可以发现
显然特别极端的情况是不能可能的,比如

这是显然可以随意调整一下的,最后最左边的点一定是选择一个离他较近的点的
想证明其他的基本同理,试图枚举下面的点与上面的值是否相同即可
然后大概感觉上就没什么问题了,四个的话也是可以枚举值是否相同做的
好像就到 3 了,可以考虑 n = 3 且 a_i b_i 都相同的时候,
显然是三个交叉选要优的,要么就无解了。。
然后就粗糙的证完了,估计考场上也就这样了
关于保证两两配对,状态定义为 f[i] 表示给前 i 个配好对之后的最小代价
只要每次转移选完当前的从前边的状态转移过来就行
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
int n;
ll a[MAXN], b[MAXN], f[MAXN];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%lld%lld", &a[i], &b[i]);
f[i] = 200000000000000ll;
}
if(n == 1 && a[1] == b[1]) {
puts("-1");
return 0;
}
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1);
f[0] = 0ll;
for(int i = 1; i <= n; ++i) {
if(a[i] != b[i]) f[i] = f[i - 1] + abs(a[i] - b[i]);
if(i >= 2 && a[i] != b[i - 1] && a[i - 1] != b[i]) f[i] = min(f[i], f[i - 2] + abs(a[i] - b[i - 1]) + abs(a[i - 1] - b[i]));
if(i >= 3) {
if((a[i] != b[i - 1]) && (a[i - 1] != b[i - 2]) && (a[i - 2] != b[i])) f[i] = min(f[i], f[i - 3] + abs(a[i] - b[i - 1]) + abs(a[i - 1] - b[i - 2]) + abs(a[i - 2] - b[i]));
if((a[i] != b[i - 2]) && (a[i - 1] != b[i]) && (a[i - 2] != b[i - 1])) f[i] = min(f[i], f[i - 3] + abs(a[i] - b[i - 2]) + abs(a[i - 1] - b[i]) + abs(a[i - 2] - b[i - 1]));
}
}
printf("%lld\n", (f[n] == 200000000000000ll ? -1 : f[n]));
return 0;
}
禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载
,用户转载请注明出处:https://www.cnblogs.com/xcysblog/

浙公网安备 33010602011771号