阿里笔试4.1场(附代码 + 3月全5场代码链接)
隔几天做两道= =保持做题感觉
前5场代码链接:
——————————————————————————————————————
再更:第一题改成一种位运算的思路,因为题设里串长小于等于20,所以bfs复杂度应该是可以满足的(~1e6)
这种思路只要复杂度满足要求,一定是正确的。
之前的做法目前版本暂时没有证伪……如果有反例请留言,我有点没想出= =
/*
把一个串恢复全0的最小步数 = 从全0变为该串的最小步数
对于某一个串,计算其1步可达的串,等价于其与7(二进制111)及其左移N位作异或
(注意收尾位的处理,我是单独处理手尾位,用3和3的左移串长-2位作异或)
如果新增串之前没有记录过,就记录其到0的距离是当前串+1,并将其加入处理队列
队列空,则所有可达串访问完毕。
如果访问到目标串,返回其步长,否则返回No
*/
#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<math.h>
using namespace std;
int str2num(string s) {
int res = s[0] - '0';
int i;
for(i = 1; i < s.length(); i++) {
res = res*2 + s[i] - '0';
}
return res;
}
int fun(string s) {
if(s.empty()) {
return INT_MAX;
}
else if(s.length() == 1) {
if(s[0] == '0') {
return 0;
}
else {
return INT_MAX;
}
}
else if(s.length() == 2) {
if(s[0] == s[1]) {
return s[0] - '0';
}
else {
return INT_MAX;
}
}
int val = str2num(s);
vector<int> mark;
queue<int> pos;
int i, tmp;
for(i = 0; i < int(pow(2, s.length())); i++) {
mark.push_back(INT_MAX);
}
mark[0] = 0;
pos.push(0);
int shiftv[s.length()];
shiftv[0] = 3;
for(i = 1; i < s.length()-1; i++) {
shiftv[i] = 7<<(i-1);
}
shiftv[s.length()-1] = 3<<(s.length()-2);
while(!pos.empty()) {
for(i = 0; i < s.length(); i++) {
tmp = pos.front()^shiftv[i];
if(tmp == val) {
return mark[pos.front()]+1;
}
else if(mark[tmp] == INT_MAX){
mark[tmp] = mark[pos.front()]+1;
pos.push(tmp);
}
}
pos.pop();
}
return mark[val];
}
int main() {
string s;
cin>>s;
int res = fun(s);
if(res == INT_MAX) {
cout<<"No"<<endl;
}
else {
cout<<res<<endl;
}
return 0;
} ——————————————————————————————————————
注:以下都是根据讨论区实现的代码,数据输入格式、数据范围都不确定,所以只供思路探讨:
第1题:(简单更新了,想问问大家还有没有新的反例)
/*
从左向右,如第i位碰到1时,反转第i,i+1,i+2位,直到完成完整串
如果全部完成后,最后一位是1,表示不能翻转出全0;否则就返回翻转次数
由于这种操作没有对同一位置的重复操作,一定是最少的
时间复杂度O(n)
*/
/*
补充:原代码没有考虑第一位需要翻转的情况,因此现在分别考虑第一位翻转和第一位不翻转两种情况,
取两者中能够成功且次数小的一种。
*/
#include<iostream>
#include<string>
#include<math.h>
using namespace std;
int fun(string s) {
if(s.empty()) {
return 0;
}
int i;
int count = 0;
for(i = 0; i < s.length()-1; i++) {
if(s[i] == '1') {
s[i] = '0';
s[i+1] = '0' + (1 - (s[i+1] - '0'));
if(i < s.length() - 2) {
s[i+2] = '0' + (1 - (s[i+2] - '0'));
}
count++;
}
}
if(s[s.length()-1] == '1') {
return INT_MAX;
}
else {
return count;
}
}
int main() {
string s;
cin>>s;
int res = fun(s), res2;
string scp;
scp = s;
if(s.length() > 2) {
scp[0] = '0';
scp[1] = '0' + (1 - (s[1] - '0'));
}
res2 = fun(scp);
if(res2 != INT_MAX) {
res = min(res, res2 + 1);
}
if(res == INT_MAX) {
cout<<"NO"<<endl;
}
else {
cout<<res<<endl;
}
return 0;
} 一个只有01的串,每次可以同时翻转i-1,i,i+1位(左右端只翻转两位),求最小翻转次数(如无法反转,则输出”No")
这道题感觉思路不确定一定对,但没想到反例……希望讨论或者举点极端的例子= =
第2题:有N个怪兽,M个弓箭,每个怪兽有生命值,每个弓箭有杀伤力和价值,每个怪兽只能用一支弓箭攻击,弓箭杀伤>=怪兽生命时可消灭怪兽,求使用弓箭的最小价值。如无法消灭,返回-1。
/*
1. 对怪兽生命从大到小排序
2. 对弓箭按攻击力从大到小排序
3. 建立一个优先队列,按照价值从小到大
之后按照顺序对每个怪兽计算:
1.找出所有攻击力大于等于其生命值的弓箭,将其加入优先队列
2.若此时队列空,证明没有可以消灭该怪兽的弓箭了,返回-1
否则将优先队列队头出队,记录其价值。
返回总价值即可。
时间复杂度O(mlogm)
*/
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
struct prs {
int cost;
int att;
};
bool cmp1 (int a, int b) {
return a > b;
}
bool cmp2(prs p1, prs p2) {
return p1.att > p2.att;
}
int mincost(vector<int> &monst,vector<prs> &arrow) {
if(monst.size() > arrow.size()) {
return -1;
}
sort(monst.begin(), monst.end(), cmp1);
sort(arrow.begin(), arrow.end(), cmp2);
priority_queue<int, vector<int>, greater<int> > pq;
int res = 0;
int i, j = 0;
for(i = 0; i < monst.size(); i++) {
while(j < arrow.size() && arrow[j].att >= monst[i]) {
pq.push(arrow[j].cost);
j++;
}
if(pq.empty()) {
return -1;
}
else {
res = res + pq.top();
pq.pop();
}
}
return res;
}
int main() {
int N, M;
cin>>N>>M;
vector<int> monst;
vector<prs> arrow;
prs ptmp;
int tmp;
int i;
for(i = 0; i < N; i++) {
cin>>tmp;
monst.push_back(tmp);
}
for(i = 0; i < M; i++) {
cin>>ptmp.cost;
arrow.push_back(ptmp);
}
for(i = 0; i < M; i++) {
cin>>arrow[i].att;
}
cout<<mincost(monst, arrow)<<endl;
return 0;
}
小天才公司福利 1225人发布