A. Most Unstable Array
(Description:)
给定 (n, m),构造出一个长度为 (n) 的数组 (a),使得数组的和为 (m),在此条件下,(sum_{i=1}^{n - 1}left | a_i - a_{i + 1} ight |) 最大是多少?
(Solution:)
显然当 (n geq 3) 的情况下,构造 (0, m, 0, 0, ...) 这样的最优,再特判 (n leq 2) 的情况即可。
(Code:)
#include <bits/stdc++.h>
using namespace std;
int main(){
int t; cin >> t;
while(t --){
int n, m; cin >> n >> m;
if(n == 1)cout << 0 << endl;
else if(n == 2) cout << m << endl;
else cout << m * 2 << endl;
}
return 0;
}
B. Two Arrays And Swaps
(Description:)
给定两个数组,你可以交换 (k) 次 两个数组的元素,问最后 (a) 数组的和最大可以是多少?
(Solution:)
将两个数组排序后,贪心的交换即可。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 40;
int a[N], b[N];
int main(){
int t; cin >> t;
while(t --){
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++) cin >> b[i];
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1);
int sum = 0;
for(int i = 1; i <= n; i ++) sum += a[i];
int idx = n;
for(int i = 1; i <= n; i ++){
if(a[i] < b[idx] && k){
k --;
sum += b[idx --] - a[i];
}else break;
}
cout << sum << endl;
}
return 0;
}
C. Board Moves
(Description:)
有一个 (n imes n) 的矩阵((n) 为奇数),每个矩阵单元有一个物品,每次操作你可将一个单元里的一个物品移动到该单元周围的八个单元里,问最后只有一个单元有物品的情况下,最少要多少次操作?
(Solution:)
我们可以将所有物品最终都汇聚到矩阵的中心的单元格,那么以此为中心,他周围的第一圈的单元格的物品只需要一步就可以,第二圈就需要两步,以此类推。
那么我们可以预处理出每一圈的单元格的数量,乘以他对应的步数,再求和即可。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
typedef long long ll;
int f[N];
int main(){
f[0] = 0;
for(int i = 1; i < N; i ++){
f[i] = f[i - 1] + 8;
}
int t; cin >> t;
while(t --){
ll n; cin >> n;
n = n * n - 1;
ll ans = 0;
for(int i = 1; ; i ++){
if(n >= f[i]){
n -= f[i];
ans += 1ll * i * f[i];
}else break;
}
cout << ans << endl;
}
return 0;
}
D. Constructing the Array
(Description:)
有长度为 (n) 的数组 (a) ,全为 (0),接下来循环 (n) 次,每次选出一段最长的连续区间 ([l, r])(全为 (0) ,如果一样长,就选最左边的)。
如果 (r - l + 1) 是奇数,那么 (a[frac{l + r}{2}] = i);
否则,(a[frac{l + r - 1}{2}] = i);((i) 是第几轮循环)。
输出最终的数组 (a)。
(Solution:)
我们可以直接暴力去做。即我们每次选出符合条件的 ([l, r]),然后对应的给 (a[i]) 赋值,又得到了两个新的更小的区间,我们需要存储下来,并按照上述的规则对所有的区间排序。显然优先队列可以完美的满足我们的要求。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct node{
int l, r;
friend bool operator < (const node &a, const node &b){
if(a.r - a.l == b.r - b.l) return a.l > b.l;
return a.r - a.l < b.r - b.l;
}
};
int a[N];
int main(){
int t; cin >> t;
while(t --){
int n; cin >> n;
priority_queue<node> q;
q.push({1, n});
int idx = 1;
while(!q.empty()){
node cur = q.top(); q.pop();
int l = cur.l, r = cur.r;
int mid;
if((r - l + 1) & 1) mid = l + r >> 1;
else mid = l + r - 1 >> 1;
a[mid] = idx ++;
if(mid - l >= 1) q.push({l, mid - 1});
if(r - mid >= 1) q.push({mid + 1, r});
}
for(int i = 1; i <= n; i ++) printf("%d ", a[i]);
puts("");
}
return 0;
}
E. K-periodic Garland
(Description:)
给定长为 (n) 的 (0, 1) 字符串,你可以通过一次操作改变一个字符((0) 变 (1) (or) (1) 变 (0)),问最少几次操作可以使任意相邻两个 (1) 之间的距离为 (k) ?
(Solution:)
显然根据题意可以得知最终所有 (1) 的位置 (mod k) 的余数 (t) 都相同。那么我们就可以去枚举 (t),判断在这种情况下,需要几次操作,最后取 (min) 即可。
首先我们要知道的是我们最多改变多少次:原字符串 (1) 的个数 (- 1) 次。
我们先算出原字符串 (1) 的个数 (cnt)。然后假设原字符串的 (1) 对应 (1),(0) 对应 (-1),那么我们将对应位即:(t, t + k, t + 2 * k, ...) 的字符提取出来,按照对应方式组成一个新序列,那么求出该序列的最大连续子段和 (cur),那么 (min(cnt - cur)) 就是答案。
我们知道只有 (t, t + k, ...) 这些位才有可能为 (1),那么我们求出的 (cur) 无非就两种情况:
(1.) (a, b, c) ((a, c)是一连串的 (-1); (b) 是一连串的 (1));
(2.) (a, b, c) ((a, c) 是一连串的 (1); (b) 是一连串的 (-1))((a + b + c > max(a, c)))。
对于 (1.) 来说,(cur) 个 (1) 是不需要改变的,那么在其他的位置多出来 (cnt - cur) 个 (1),所以需要 (cnt - cur) 次来把他们变为 (0)。
对于 (2.) 来说,有 (a + c) 个 (1) 是不需要改变的,我们需要把中间的 (b) 个 (0) 变为 (1),还需要把 (cnt - a - c) 个其他位置的 (1) 变为 (0)。即 (b + cnt - a - c = cnt - cur(cur = a + c + b))。
求出最大子段和其实就是最多不需要改变的个数(新序列)。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
char s[N];
int main(){
int t; cin >> t;
while(t --){
int n, k;
cin >> n >> k;
scanf("%s", s);
int cnt = 0;
for(int i = 0; i < n; i ++)
cnt += s[i] - '0';
int ans = 0;
for(int i = 0; i < k; i ++){
int cur = 0;
for(int j = i; j < n; j += k){
cur += s[j] == '1' ? 1 : -1;
if(cur < 0) cur = 0;
ans = max(ans, cur);
}
}
cout << cnt - ans << endl;
}
return 0;
}
F. Decreasing Heights
(Description:)
给定 (n imes m) 的矩阵,要求你从 (a_{1,1}) 走到 (a_{n,m}),规定只用每次向右或向下走一格,并且如果从一格 (a_{i,j}) 移动到另一格 (a_{k,l}) 要求 (a_{i,j} + 1 = a_{k,l})。你可以进行一个操作为:让某一格上的值 (- 1)。
求最少的操作次数?
(Solution:)
假设我们走的一条路径为:(a_1, a_2, a_3,...,a_k),那么我们对每一格的操作次数为:(t_1, t_2, t_3,...,t_k)。
那么显然 (a_i - t_i + 1 = a_{i+1} - t_{i+1})。观察等式我们发现两边可以同时加上 (min(t_i, t_{i+1})),等式仍然成立。那么推广到 (k) 个点,我们每个点的操作次数都可以减去 (min(t_1, t_2, t_3,...,t_k))。这样显然优化了我们的答案。
那么我们可以得出:对于任意一条路径而言,都应该存在一个点的值没有发生改变。
根据上面的结论,我们就可以去枚举那个没有改变的点,然后根据这个点,我们可以算出其他点改变后的值。
例如:假设定点为 (a_{i,j}),对于 (a_{x,y}),与 (a_{i,j}) 的步长 (d = abs(x - i) + abs(y - j))。
如果 (a_{x,y}) 在定点的左上角,那么 (b_{x,y} = a_{i,j} - d)。
如果 (a_{x,y}) 在定点的右下角,那么 (b_{x,y} = a_{i,j} + d)。
其他位置显然是不合法的,并且如果 (a_{x,y} < b_{x,y}) 也是不合法的(因为只能减,不能加)。
算出他改变后的值,我们就可以算出对于该点的操作次数,即代价。
那么到此,就是一个很简单的二维矩阵的 (dp) 了。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 10;
const long long INF = 1e18;
typedef long long ll;
ll a[N][N], f[N][N];
int main(){
int t; cin >> t;
while(t --){
ll ans = INF;
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
scanf("%lld", &a[i][j]);
// 枚举定点
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++){
for(int x = 0; x <= n; x ++)
for(int y = 0; y <= m; y ++)
f[x][y] = INF;
f[1][0] = 0;
for(int x = 1; x <= n; x ++)
for(int y = 1; y <= m; y ++){
ll d = abs(x - i) + abs(y - j); // 计算步长
if(x <= i && y <= j && a[x][y] >= a[i][j] - d){
f[x][y] = min(f[x-1][y], f[x][y-1])+a[x][y]-a[i][j]+d;
}else if(x >= i && y >= j && a[x][y] >= a[i][j] + d){
f[x][y] = min(f[x-1][y], f[x][y-1])+a[x][y]-a[i][j]-d;
}
}
ans = min(ans, f[n][m]);
}
cout << ans << endl;
}
return 0;
}