开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情
第 320 场周赛 - 力扣(LeetCode)
2475. 数组中不等三元组的数目
问题解析
因为数组长度才100,直接用三层for循环枚举i,j,k,记录nums[i]和nums[j]和nums[k]都不相同的个数即可。
AC代码
class Solution {
public:
int unequalTriplets(vector<int>& nums) {
int n=nums.size(),cnt=0;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
for(int k=j+1;k<n;k++)
if(nums[i]!=nums[j]&&nums[j]!=nums[k]&&nums[i]!=nums[k])
cnt++;
return cnt;
}
};
2476. 二叉搜索树最近节点查询
问题解析
注意,题目说的此树是一个二叉搜索树,二叉搜索树的特点就是,root的左孩子小于root,右孩子大于root。
由于这个特点,它的中序序列是一个单调递增的数组,我们可以先中序遍历二叉搜索树并将它的中序序列记录下来,之后每次询问直接二分查找即可。
AC代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int>v;
void dfs(TreeNode*root)
{
if(!root)return;
dfs(root->left);
v.push_back(root->val);
dfs(root->right);
}
vector<vector<int>> closestNodes(TreeNode* root, vector<int>& queries) {
dfs(root);
vector<vector<int>>res;
for(auto&i:queries)
{
vector<int>ans;
int l=0,r=v.size()-1;
while(l<r)
{
int mid=(l+r+1)/2;
if(v[mid]<=i)l=mid;
else r=mid-1;
}
if(v[l]<=i)
ans.push_back(v[l]);
else ans.push_back(-1);
l=0,r=v.size()-1;
while(l<r)
{
int mid=(l+r)/2;
if(v[mid]>=i)r=mid;
else l=mid+1;
}
if(v[l]>=i)
ans.push_back(v[l]);
else ans.push_back(-1);
res.push_back(ans);
}
return res;
}
};
2477. 到达首都的最少油耗 - 力扣(LeetCode)
问题解析
从示例二中我们就能看出,最好的做法就是每个城市的车尽量坐满人后再前往下个城市。
- 先按照边建树,我们以0为根dfs整个树;
- 因为是以0为起点,所以我们先走到叶子处后,回头的路上再记录汽油;
- 每次把一整个城市的人一起移动到下一个城市,而不是一个一个的直接送到首都。
AC代码
class Solution {
public:
typedef long long ll;
vector<int>tree[100050];
ll f[100050],cnt=0,up;
void dfs(int x,int y)
{
for(auto&i:tree[x])
{
if(i==y)continue;
dfs(i,x);
f[x]+=f[i];
}
if (x == 0)return;
if(f[x]%up==0)cnt+=f[x]/up;
else cnt+=f[x]/up+1;
}
long long minimumFuelCost(vector<vector<int>>& roads, int seats) {
int n=roads.size();
for(int i=1;i<=n;i++)f[i]=1;
for(auto&i:roads)
{
tree[i[0]].push_back(i[1]);
tree[i[1]].push_back(i[0]);
}
up=seats;
dfs(0,-1);
return cnt;
}
};
2478. 完美分割的方案数 - 力扣(LeetCode)
问题解析
很容易看出的线性dp。
设立二维状态转移数组f,f[i] [j]表示:以第i个字符为结尾,且当前是分割出的第j段的方案数,结果就是f[n] [k]。
- 如果第i个数是一个非质数,且它后面的数字是一个质数(或者这个数就是最后一个数了),那么这个数就是一个合格的结尾。
- 我们在前i-minLength个数(因为切割出的字段长度要为minLength)里找所有的质数来作这一段切割的开头。
- 假如第x个数是质数,那么就有状态转移方程:f[i] [j]+= f[x-1] [j-1];
但是,我们枚举结尾复杂度是O(n),枚举结尾同时寻找开头复杂度也是O(n),找到了开头还要滚动k,这样总的复杂度是O(n*n *k);显然不太行,所以我们要优化一下:
可以注意到实际上,我们滚动k的过程中,就是把前i-minLength个数分割的结果加到第i个数上。那么我们可以用一个前缀和数组记录下之前的所有结果,这样就可以不用往前一个个找质数了。
AC代码
class Solution {
public:
int MOD=1e9+7;
long long f[1050][1050],a[1050],sum[1050][1050];
int beautifulPartitions(string s, int k, int minLength) {
unordered_map<int, int>mymap;
map<int, int>pos;
mymap[2] = mymap[3] = mymap[5] = mymap[7] = 1;
if (!mymap.count(s[0] - '0'))return 0;
int n = s.size();
for (int i = 1; i <= n; i++)
a[i] = s[i - 1] - '0';
f[0][0] = 1;
sum[0][0] = 1;
for (int i = 1; i <= n; i++)
{
if (i>=minLength&&!mymap.count(a[i])&&(i==n||mymap.count(a[i+1])))
{
for(int j=1;j<=k;j++)
f[i][j]=(f[i][j]+sum[i-minLength][j-1])%MOD;
}
for (int j = 0; j < k; j++)
{
sum[i][j] = (sum[i-1][j] + f[i][j]) % MOD;
}
}
return f[n][k] % MOD;
}
};