A. Make it Beautiful
给出一个数组,将它重新排列,使得它成为一个beautiful数组。ugly数组的定义是存在一个数,为前面所有数字的和。
思路:升序排序后一前一后构造数组,最后判断一下即可。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e5 + 5;
int t, n;
int a[N], dif[N];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n;
for(int i = 0; i <= n; i ++) {
dif[i] = 0;
}
for(int i = 1; i <= n; i ++) {
std::cin >> a[i];
}
int l = 1, r = n;
std::vector<int> vec;
vec.push_back(0);
while(l <= r) {
vec.push_back(a[l]);
vec.push_back(a[r]);
l ++;
r --;
}
bool flag = true;
for(int i = 1; i <= n; i ++) {
dif[i] = dif[i - 1] + vec[i];
if(vec[i] == dif[i - 1]) {
flag = false;
break;
}
}
if(!flag) {
std::cout << "NO" << '\n';
continue;
}
std::cout << "YES" << '\n';
for(int i = 1; i <= n; i ++) {
std::cout << vec[i] << " \n"[i == n];
}
}
return 0;
}
B. Matrix of Differences
给出n * n的矩阵,用1~n * n数字填入其中,使得矩阵中相邻两数之差的种类最多。
思路:观察样例可得,最多的种类就是1~n * n - 1,这样可以有种构造方式,从一半大小数字开始,每次加减一个递增的数字,例如4 * 4,我们可以构造这样的序列:8,9, 7,10,6,11,5,12,4,13,3,14,2,15,1,16,按照这样的序列,蛇形来回填数即可。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 55;
int t, n;
int a[N][N];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n;
int num = (n * n + 1) / 2, pos = 1, cnt = 1;
for(int i = 1; i <= n; i ++) {
if(i & 1) {
for(int j = 1; j <= n; j ++) {
if(pos)
a[i][j] = num, num += cnt, cnt ++, pos ^= 1;
else
a[i][j] = num, num -= cnt, cnt ++, pos ^= 1;
}
}
else {
for(int j = n; j >= 1; j --) {
if(pos)
a[i][j] = num, num += cnt, cnt ++, pos ^= 1;
else
a[i][j] = num, num -= cnt, cnt ++, pos ^= 1;
}
}
}
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
std::cout << a[i][j] << " \n"[j == n];
}
}
}
return 0;
}
C. Yet Another Tournament
一共可以准备m分钟,在一场对决中,只有准备时间大于等于a[i]才能胜利,最后按照胜利的轮数排序,求能达到的最好的名次是多少。
思路:注意,第i个人的攻击力为i,则说明,如果不加入自己的话,第i个人会赢i - 1场次!(读错题了,一直wa到结束)所以贪心得到最多可以赢的人数,使得自己赢的场次最多,假设能赢k个人,此时如果想要排名上升,则需要打败第k + 1个人,注意,这个第k + 1个人指的是原数组中第k + 1个人,这个人的攻击力为k + 1,能赢k场。判断一下剩余的时间和前面的时间能否打败这个人,能打败则与该人并列第n - k名,否则在他后面一名。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 5e5 + 5;
int t, n;
ll m;
int b[N], a[N];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n >> m;
for(int i = 0; i <= n + 1; i ++) {
b[i] = 0, a[i] = 0;
}
for(int i = 1; i <= n; i ++) {
std::cin >> a[i];
b[i] = a[i];
}
std::sort(a + 1, a + 1 + n);
int cnt = 0;
for(int i = 1; i <= n; i ++) {
if(m >= a[i]) {
m -= a[i];
cnt ++;
}
}
int ans = n - cnt + 1;
if(cnt + 1 <= n && b[cnt + 1] <= m + a[cnt])
ans --;
std::cout << ans << '\n';
}
return 0;
}
os:好离谱啊,赛时一直读假题
D. Different Arrays
给出序列a,对于a[2] ~ a[n - 1]进行n - 2次操作,每次操作可以将与a[i]相邻的两个数一个加上a[i],一个减去a[i],问最后能得到多少不同的序列。
思路:考虑动态规划。数字对于前面的数字的修改无后效性,但是对后面的额数字修改有后效性,根据此进行转移,令f[i][j]表示到前i个数,该数等于j的方案数。转移方程表示为:
f[i + 1][a[i + 1] - j] += f[i][j];
f[i + 1][a[i + 1] +j] += f[i][j];
很显然,转移方程即对于后面的数字进行的两种操作的转移。通过枚举位置和能够到达的数字j,j可以是[-300 * 300,300 * 300],复杂度可以通过。
但是存在一个特殊情况,当j == 0时,两个转移方程相同,会使得操作进行两遍,显然是不对的,特判即可。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 305;
const int mod = 998244353;
const int M = 300 * 300;
int n;
int a[N];
ll f[N][(M << 1) + 50];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> n;
for(int i = 1; i <= n; i ++) {
std::cin >> a[i];
}
f[2][a[2] + M] = 1;
for(int i = 2; i < n; i ++) {
for(int j = -M; j <= M; j ++) {
f[i + 1][a[i + 1] - j + M] = (f[i + 1][a[i + 1] - j + M] + f[i][j + M]) % mod;
if(j)
f[i + 1][a[i + 1] + j + M] = (f[i + 1][a[i + 1] + j + M] + f[i][j + M]) % mod;
}
}
ll ans = 0;
for(int i = -M; i <= M; i ++) {
ans = (ans + f[n][i + M]) % mod;
}
std::cout << ans << '\n';
return 0;
}