排序。
将数组 nums 从小到大排序。
每一轮,Alice 先从 nums 中移除一个最小元素,然后 Bob 执行同样的操作。接着,Bob 会将移除的元素添加到数组 arr 中,然后 Alice 也执行同样的操作。
对排序后的数组的每两个元素做交换,再到下一组两个元素,直至数组末尾。
/*
* @lc app=leetcode.cn id=2974 lang=cpp
*
* [2974] 最小数字游戏
*/
// @lc code=start
class Solution
{
public:
vector<int> numberGame(vector<int> &nums)
{
// 特判
if (nums.empty())
return {};
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i += 2)
swap(nums[i], nums[i + 1]);
return nums;
}
};
// @lc code=end
时间复杂度:O(nlogn),其中 n 是数组 nums 的长度。
空间复杂度:O(1)。
哈希 + 暴力枚举。
水平栅栏和垂直栅栏分开计算。
答案就是 hDistances 和 vDistances 交集中的最大值的平方。如果不存在最大值,返回 −1。
/*
* @lc app=leetcode.cn id=2975 lang=cpp
*
* [2975] 移除栅栏得到的正方形田地的最大面积
*/
// @lc code=start
class Solution
{
private:
const int MOD = 1e9 + 7;
public:
int maximizeSquareArea(int m, int n, vector<int> &hFences, vector<int> &vFences)
{
// 特判
if (m == n)
return (long long)(m - 1) * (n - 1);
if (hFences.empty() || vFences.empty())
return pow(min(m, n) - 1, 2);
// 预处理
hFences.push_back(1), hFences.push_back(m);
vFences.push_back(1), vFences.push_back(n);
// 得到距离
unordered_set<int> hDistances = calDistances(hFences);
unordered_set<int> vDistances = calDistances(vFences);
// 计算正方形田地的最大面积
int max_len = 0;
for (int h_distance : hDistances)
if (vDistances.contains(h_distance))
max_len = max(max_len, h_distance);
return max_len == 0 ? -1 : (long long)max_len * max_len % MOD;
}
// 辅函数 - 计算任意两个栅栏之间的距离
unordered_set<int> calDistances(vector<int> &fences)
{
unordered_set<int> distances;
sort(fences.begin(), fences.end());
for (int i = 0; i < fences.size() - 1; i++)
for (int j = i + 1; j < fences.size(); j++)
distances.insert(fences[j] - fences[i]);
return distances;
}
};
// @lc code=end
时间复杂度:O(h2+v2),其中 h 是数组 hFences 的长度,v 是数组 hFences 的长度。
空间复杂度:O(h2+v2),其中 h 是数组 hFences 的长度,v 是数组 hFences 的长度。
建图,从 original[i] 向 changed[i] 连边,边权为 cost[i]。没边的边权设为 INF。
然后用 Floyd 算法求图中任意两点最短路,得到 g 矩阵。这里得到的 g[i][j] 表示字母 i 通过若干次替换操作变成字母 j 的最小成本。
最后累加所有 g[original[i]],即为答案。如果中间遇到边权为 INF,说明无法转换,返回 −1。
/*
* @lc app=leetcode.cn id=2976 lang=cpp
*
* [2976] 转换字符串的最小成本 I
*/
// @lc code=start
class Solution
{
private:
const int INF = 1e9;
public:
long long minimumCost(string source, string target, vector<char> &original, vector<char> &changed, vector<int> &cost)
{
// 邻接矩阵
vector<vector<long long>> g(26, vector<long long>(26, INF));
// 初始化
for (int i = 0; i < 26; i++)
g[i][i] = 0;
// 建图
for (int i = 0; i < original.size(); i++)
{
int x = original[i] - 'a', y = changed[i] - 'a';
g[x][y] = min(g[x][y], (long long)cost[i]);
}
// Floyd 算法求最短路
for (int k = 0; k < 26; k++)
for (int i = 0; i < 26; i++)
for (int j = 0; j < 26; j++)
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
// 计算最小成本
long long minCost = 0;
for (int i = 0; i < source.size(); i++)
{
int x = source[i] - 'a', y = target[i] - 'a';
if (x != y)
{
// x 不能变成 y,无解
if (g[x][y] >= INF)
return -1;
// 否则答案增加把 x 改成 y 的最小代价
minCost += g[x][y];
}
}
return minCost;
}
};
// @lc code=end
时间复杂度:O(n+m+∣Σ∣3),其中 n 为字符串 source/target 的长度,m 为数组 original/changed/cost 的长度,∣Σ∣ 为字符集合的大小,本题中字符均为小写字母,所以 ∣Σ∣=26。
空间复杂度:O(∣Σ∣2),其中 ∣Σ∣ 为字符集合的大小,本题中字符均为小写字母,所以 ∣Σ∣=26。
/*
* @lc app=leetcode.cn id=2977 lang=cpp
*
* [2977] 转换字符串的最小成本 II
*/
// @lc code=start
struct Node
{
Node *son[26]{};
int sid = -1; // 字符串的编号
};
class Solution
{
public:
long long
minimumCost(string source, string target, vector<string> &original, vector<string> &changed, vector<int> &cost)
{
Node *root = new Node();
int sid = 0;
auto put = [&](string &s) -> int
{
Node *o = root;
for (char b : s)
{
int i = b - 'a';
if (o->son[i] == nullptr)
o->son[i] = new Node();
o = o->son[i];
}
if (o->sid < 0)
o->sid = sid++;
return o->sid;
};
// 初始化距离矩阵
int m = cost.size();
vector<vector<int>> dis(m * 2, vector<int>(m * 2, INT_MAX / 2));
for (int i = 0; i < m * 2; i++)
dis[i][i] = 0;
for (int i = 0; i < m; i++)
{
int x = put(original[i]);
int y = put(changed[i]);
dis[x][y] = min(dis[x][y], cost[i]);
}
// Floyd 求任意两点最短路
for (int k = 0; k < sid; k++)
{
for (int i = 0; i < sid; i++)
{
if (dis[i][k] == INT_MAX / 2) // 加上这句话,巨大优化!
continue;
for (int j = 0; j < sid; j++)
{
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
int n = source.size();
vector<long long> dp(n + 1);
for (int i = n - 1; i >= 0; i--)
{
// 不修改 source[i]
dp[i] = source[i] == target[i] ? dp[i + 1] : LONG_LONG_MAX / 2;
Node *p = root, *q = root;
for (int j = i; j < n; j++)
{
p = p->son[source[j] - 'a'];
q = q->son[target[j] - 'a'];
if (p == nullptr || q == nullptr)
break;
if (p->sid < 0 || q->sid < 0)
continue;
// 修改从 i 到 j 的这一段
int d = dis[p->sid][q->sid];
if (d < INT_MAX / 2)
dp[i] = min(dp[i], dis[p->sid][q->sid] + dp[j + 1]);
}
}
return dp[0] < LONG_LONG_MAX / 2 ? dp[0] : -1;
}
};
// @lc code=end
时间复杂度:O(n2+mn+m3),其中 n 是字符串 source 的长度,m 是数组 cost 的长度。动态规划需要 O(n2) 的时间,把 2m 个长度至多为 n 的字符串插入字典树需要 O(mn) 的时间,Floyd 算法需要 O(m3) 的时间。
空间复杂度:O(n+mn+m2),其中 n 是字符串 source 的长度,m 是数组 cost 的长度。动态规划需要 O(n) 的空间,把 2m 个长度至多为 n 的字符串插入字典树需要 O(mn) 的空间,Floyd 算法需要 O(m2)的空间。