蚂蚁笔试研发编程题20220404
1. 正直者、欺诈者
简单模拟即可.
#include <iostream>
#include <string>
using namespace std;
int main() {
int n;
string s;
cin >> n >> s;
int q;
cin >> q;
int x, y;
while(q--){
cin >> x >> y;
x = x-1, y = y-1;
if(s[x] == 'H'){
cout << (s[y] == 'H' ? "honester" : "liar");
} else {
cout << (s[y] == 'L' ? "honester" : "liar");
}
cout << endl;
}
return 0;
}
2. 子树问题
后序遍历判断所有子树是否均为红色,并且判断当前节点是否为红色,递归返回。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int ans = 0;
bool dfs(int u, int fa, const vector<vector<int>> &edges, const string& s){
bool flag = true; //当前节点的所有孩子节点是否均为红色。
for (auto v : edges[u]) {
if (v == fa) continue;
flag &= dfs(v, u, edges, s);
}
if(flag && s[u-1] == 'R') {
ans++;
return true;
}
return false;
}
//Q2
int main(){
int n;
cin >> n;
string s;
cin >> s;
vector<vector<int>> edges(n+1, vector<int>());
int u, v;
for (int i = 0; i < n-1; ++i) {
cin >> u >> v;
edges[u].push_back(v);
edges[v].push_back(u);
}
dfs(1, 0, edges, s);
cout << ans << endl;
return 0;
}
3. k-好数
笔试过程中没有写出来,只通过了暴力的25%。 这里给出后面想的思路,不清楚是否能A掉,可能有些地方讲不明白,见谅。
题目中k-好数可以转换为如下定义:对于任意的十进制数x,其对应的k进制数中是否只有0和1两种数字,即:
反向转换为十进制数:
可以看出题目要求与上述定义的等价性。
因此需要一个进制转换的函数:
//将x转换为k进制数,数组下标从小到大依次对应低位到高位的进制数a_i
//typedef long long ll
vector<ll> kPresent(ll x, ll k){
vector<ll> ans;
while(x >= k){
ans.push_back(x % k);
x/=k;
}
if (x != 0)
ans.push_back(x);
return ans;
}
但是如何找到[l,r]中满足条件的数,并得到个数?
最简单的方法就是暴力,对于[l,r]中所有的数进行进制转换并判断,但这样超时了。
当时想到的方法是:首先找到区间中第一个满足条件的数x,以及最后一个满足条件的数y,并得到他们对应的k进制表示:
,显然
因为均只有0和1,因此可以把这个k进制表示看做2进制表示,那么其他满足条件的数的k进制表示就在这两个
二进制表示之间。那么一共就有
个数满足条件
有点说不明白,举个例子,就用测试用例来说,[15,21] 第一个满足条件的数是16,其4进制表示为:(100)4 最后一个满足条件的数是21, 其4进制表示为:(111)4 那么其他满足条件的数的4进制表示就还剩下:(101)4,(110)4。加起来一共4个数,即:(111)2 - (100)2 + 1 = 7 - 4 + 1 = 4个
笔试中利用循环去查找第一个数和最后一个数,导致超时了。以下是后面想到的在O(1)时间内找到满足条件的第一个数和最后一个数的方法:
找到第一个满足条件的数:
- 计算l的k进制表示,要找到第一个大于等于l且k进制表示只有0和1的数
- 从k进制表示的高位往低位进行判断,找到第一个大于1的位置idx
- 将比idx位数低的所有数置为0
- 从下标idx开始往高位进位,并置0,直到遇到第一个0,将其置为1。
- 此时得到的二进制表示就是大于等于l的且第一个满足条件的k-好数
找到最后一个满足条件的数:
- 计算r的k进制表示,要找到第一个小于等于r且k进制表示只有0和1的数
- 从k进制表示的高位往低位进行判断,找到第一个大于1的位置idx
- 将下标idx以及低位所有数置为1,得到的数就是最后一个满足条件的k-好数
然后通过上面的方法做一次减法即可,代码如下:
#include <iostream>
#include <vector>
#include <string>
typedef long long ll;
using namespace std;
vector<ll> kPresent(ll x, ll k){
vector<ll> ans;
while(x >= k){
ans.push_back(x % k);
x/=k;
}
if (x != 0)
ans.push_back(x);
return ans;
}
int main(){
int q;
cin >> q;
while(q--){
ll l, r, k;
cin >> l >> r >> k;
//怎么快速找到第一个数
//先得到l和r的k进制表示,均是从低位到高位
auto v1 = kPresent(l, k);
auto v2 = kPresent(r, k);
//找到第一个大于等于l的且k进制中只有0、1的数
//直接对原数组操作
auto upper = [](vector<ll> &v) -> void {
int n = v.size();
//从高位开始找,找到第一个大于1的数字,然后置0,并将下一位+1,以此重复,直到遇到0
int idx = -1; //最高位不满足条件的下标
for (int i = n-1; i >=0 ; --i) {
if(v[i] > 1) {
idx = i;
break;
}
}
if(idx == -1) return; //本身满足条件
//高位存在一个非1的值,将低位全部置为0
for (int i = 0; i < idx; ++i) {
v[i] = 0;
}
//将高位进位
int carry = 0;
for (int i = idx; i < n; ++i) {
if (v[i] + carry > 1) {
v[i] = 0;
carry = 1;
} else if (v[i] + carry == 1){
v[i] = 1;
carry = 0;
}
}
if (carry == 1) {
v.push_back(1);
}
};
//找到第一个小于等与r的且k进制中只有0、1的数
auto lower = [](vector<ll> &v) -> void {
int n = v.size();
int idx = -1; //最高位不满足条件的下标
for (int i = n-1; i >=0 ; --i) {
if(v[i] > 1) {
idx = i;
break;
}
}
if(idx == -1) return; //本身满足条件
//令v[idx] = 1,并将低位全部置1即可
for (int i = 0; i <= idx; ++i) {
v[i] = 1;
}
};
upper(v1);
lower(v2);
auto func = [](const vector<ll>& v)->ll{
ll i=1;
ll ans = 0;
for(auto c : v){
ans += c * i;
i *= 2;
}
return ans;
};
//转换为十进制做减法
cout << func(v2) - func(v1) + 1 << endl;
}
return 0;
}
#我的实习求职记录#

