G-Game of Swapping Numbers(思维)
题意:
给定一个两个数组 A [ ] , B [ ] A[], B[]A[],B[] ,定义一种操作:s w a p ( A [ i ] , A [ j ] ) ∣ ( 1 ≤ i < j ≤ n ) swap(A[i], A[j])\ | \ (1 \leq i < j \leq n)swap(A[i],A[j]) ∣ (1≤i<j≤n) 。现要求对一个数组 A AA 进行 K KK 次操作,使得 ∑ i = 1 n ∣ A i − B j ∣ \sum_{i= 1}^{n}|A_i - B_j|∑i=1n∣Ai−Bj∣ 最大。
思路:
将 [ A i , B i ] [A_i, B_i][Ai,Bi] 与 [ A j , B j ] [A_j, B_j][Aj,Bj] 看成两个区间,再数轴上画出来,再交换 A i , A j A_i, A_jAi,Aj。
可以发现,如果两个区间相交,则交换后,贡献不变(这个可以用于多次无效交换);
若区间不相交,那么交换后,贡献增加 2 ∗ m a x ( A i , A j ) − m i n ( B i , B j ) ∣ ( A i < B i & A j < B j ) 2 * max(A_i, A_j)-min(B_i,B_j) \ | \ (A_i < B_i \& A_j<B_j)2∗max(Ai,Aj)−min(Bi,Bj) ∣ (Ai<Bi&Aj<Bj)
多画几个,可以发现,其实不管 A i A_iAi 与 B i B_iBi 的大小如何,增加的贡献总是两个区间之间的距离 ∗ 2 * 2∗2。
所以可以将 A i = m a x ( A i , B i ) , B i = m a x ( A i , B i ) A_i = max(A_i,B_i), B_i=max(A_i,B_i)Ai=max(Ai,Bi),Bi=max(Ai,Bi)
考虑贪心,每次取区间间距最大的两个区间进行交换,直到没有不相交的区间,剩余操作次数直接在相交区间上进行无效操作来补充。
注意特判,当 n = 2 n = 2n=2 时,要特判,因为要此时没有无效操作可以来补充交换次数。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
int n, k;
scanf("%d%d", &n, &k);
vector<ll> a(n), b(n);
for (int i = 0; i < n; i++) {
scanf("%lld", &a[i]);
}
ll ans = 0;
for (int i = 0; i < n; i++) {
scanf("%lld", &b[i]);
ans += abs(a[i] - b[i]);
}
vector<ll> aa(n), bb(n);
for (int i = 0; i < n; i++) {
aa[i] = min(a[i], b[i]);
bb[i] = max(a[i], b[i]);
}
sort(aa.begin(), aa.end(), less<ll>());
sort(bb.begin(), bb.end());
for (int i = 0; i < min(n, k); i++) {
if (aa[i] >= bb[i]) {
ans += 2LL * (aa[i] - bb[i]);
}
else break;
}
printf("%lld\n", ans);
return 0;
}
/*
3 2
1 2 3
3 2 1
*/
K-Knowledge Test about Match(贪心)
题意:
给定 a = { 0 , 1 , 2 , 3 , … , n − 1 } , b = { b 0 , b 1 , … , b n − 1 } a=\{0, 1, 2, 3, \dots,n - 1 \}, b=\{b_0,b_1,\dots,b_{n-1}\}a={0,1,2,3,…,n−1},b={b0,b1,…,bn−1}。 你可以对 b bb 数组进行重排操作
使得 ∑ i = 0 n − 1 ∣ a i − b i ∣ \sum_{i=0}^{n-1} \sqrt{|a_i - b_i|}∑i=0n−1∣ai−bi∣ 最小。误差可以在 4 % 4\%4% 以内。
思路:
因为 x \sqrt{x}x 的函数图像在 x xx 很大时,增长得非常慢,而在 x xx 较小的时候,增长幅度非常大。
所以考虑贪心,先匹配差值为 0 00 的对,在匹配差值为 1 11 的对,… \dots… 最后匹配差值为 n − 1 n-1n−1 的对 。
还有一个奇怪的做法:
每次枚举所有点对,计算交换的后的贡献是增加还是减少,若减少,就交换。(O ( n 2 ) O(n^2)O(n2))做 15 1515 次。
虽然不懂,但我大受震撼
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
vector<int> b(n), vis(n), ans(n, -1);
for (int i = 0; i < n; i++) {
scanf("%d", &b[i]);
vis[b[i]]++;
}
for (int s = 0; s < n; s++) {
for (int i = 0; i < n; i++) {
if (vis[i] > 0 && i - s >= 0 && ans[i - s] == -1) {
ans[i - s] = i;
vis[i]--;
}
if (vis[i] > 0 && i + s < n && ans[i + s] == -1) {
ans[i + s] = i;
vis[i]--;
}
}
}
for (int i = 0; i < n; i++)
printf("%d ", ans[i]);
printf("\n");
}
return 0;
}
/*
2
10
4 8 9 9 6 1 3 9 5 2
54
0 0 0 1 2 2 4 4 5 6 7 10 16 17 18 19 19 20 23 24 25 26 26 26 27 29 30 30 31 32
32 33 34 36 37 37 38 39 41 42 42 43 43 44 44 45 45 46 50 50 51 53 53 53
*/