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/