https://www.nowcoder.com/practice/bfd8234bb5e84be0b493656e390bdebftpId=37&&tqId=21284&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking
题目描述
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
输入
每个用例包含二个整数M和N。0<=m<=10,1<=n<=10。
样例输入
7 3
样例输出
8
输入描述:
输入两个int整数
输出描述:
输出结果,int型
示例1
输入
7 3
输出
8
一开始的想法是对于每一个盘子,依次放0~m个苹果,然后考察剩下的苹果放到剩下的盘子里的情况,最后只剩1个盘子时返回1,即只有1种方法。
//错误,求的是排列数
int memo[11][11];
int calc(int m, int n)
{
if(n == 1) return 1;
if(memo[m][n] > -1) return memo[m][n];
int ans = 0;
for(int i = 0; i <= m; i++)
{
ans += calc(m - i, n - 1);
}
return memo[m][n] = ans;
}
但是这样得到的是不同放法的排列数,题目要的是不考虑顺序的放法总数。因此这种做法是错误的。
正确的做法是:
基础的情况是有m = 0或者n = 1,只有1种放法。
其它的情况分成两类:
(1) 有至少一个盘子是空的,那么放法等价于 f(m, n-1)。
(2) 所有盘子都至少有1个苹果(m >= n),那么放法等价于 f(m-n, n)。
所以, f(m, n) = f(m, n-1) + f(m-n, n)。
#include
using namespace std;
int memo[11][11];
int calc(int m, int n)
{
if(m == 0 || n == 1) return 1;
if(memo[m][n] > -1) return memo[m][n];
return memo[m][n] = calc(m, n-1) + (m >= n ? calc(m-n, n) : 0);
}
int main(){
int m, n;
memset(memo, -1, sizeof(memo));
while(cin >> m >> n)
{
if(m < 0 || n <= 0) printf("-1\n");
else printf("%d\n", calc(m, n));
}
return 0;
}
这种思路比较难想到,评论区有另外一种思路:使用规定顺序的深搜。
即按顺序在每一个盘子上分配,但是规定当前盘子的苹果数不超过上一个盘子里的苹果数,这样可以避免计算不同的排列。
#include
using namespace std;
int dfs(int rest, int idx, int maxnum, int n)
{
//苹果放完了
if(rest == 0) return 1;
//苹果没放完,盘子用完了
if(idx == n) return 0;
int ans = 0;
//maxnum: 当前盘子最多能放的苹果数
for(int i = maxnum; i >= 0; i--)
{
ans += dfs(rest - i, idx + 1, i, n);
}
return ans;
}
int main(){
int m, n;
while(cin >> m >> n)
{
if(m < 0 || n < 1) printf("-1\n");
else printf("%d\n", dfs(m, 0, m, n));
}
return 0;
}
https://www.nowcoder.com/practice/a30bbc1a0aca4c27b86dd88868de4a4atpId=37&&tqId=21269&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking
题目描述
编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。但是要保证汉字不被截半个,如"我ABC"4,应该截为"我AB",输入"我ABC汉DEF"6,应该输出为"我ABC"而不是"我ABC+汉的半个"。
输入描述:
输入待截取的字符串及长度
输出描述:
截取后的字符串
示例1
输入
我ABC汉DEF 6
输出
我ABC
判题系统采用的是GBK编码,在GBK编码格式中,一个汉字占两个字节,并且这两个字节的首位都是1,也就是转化成整数值是负数。根据这个条件,可以确定最后一个字节是半个汉字还是ASCII字符。如果是ASCII字符,那么直接输出[0...n-1]所有的字节,即str.substr(0, n)。如果最后一个字节是半个汉字,那么还需要检查[0...n-2]范围内有多少个半个汉字,如果是偶数个,那么舍去最后一个字节, n--。最后输出str.substr(0,n)。
#include
using namespace std;
int main(){
string str;
int num;
while(cin >> str >> num)
{
if(str[num-1] < 0)
{
int cnt = 0;
for(int i = 0; i < num - 1; i++)
{
if(str[i] < 0) cnt++;
}
if(!(cnt & 1)) num--;
}
printf("%s\n", str.substr(0, num).c_str());
}
return 0;
}
事实上,substr()函数会自动舍弃不完整的汉字,所以直接使用str.substr(0,n)也没问题。
https://www.nowcoder.com/practice/1221ec77125d4370833fd3ad5ba72395tpId=37&&tqId=21260&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking
有一只兔子,从出生后第3个月起每个月都生一只兔子,小兔子长到第三个月后每个月又生一只兔子,假如兔子都不死,问每个月的兔子总数为多少?
输入描述:
输入int型表示month
输出描述:
输出兔子总数int型
示例1
输入
9
输出
34
本质上是斐波那契问题,第n个月的兔子来自两个部分,第n-1个月已经有的兔子数,第n-2个月的兔子每个都会生出一只新兔子。所以 f(n) = f(n-1) + f(n-2)。
#include
using namespace std;
int helper(int n)
{
if(n <= 0) return 0;
vector dp(n+1, 0);
dp[1] = 1;
for(int i = 2; i <= n; i++)
{
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
int main(){
int n;
while(cin >> n)
{
printf("%d\n", helper(n));
}
return 0;
}
https://www.nowcoder.com/practice/49e772ab08994a96980f9618892e55b6tpId=37&&tqId=21280&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking
字符串形式的 + - * 运算。
加法:
string add(string& a, string& b)
{
if(a.size() < b.size()) a.swap(b);
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
while(b.size() < a.size()) b += '0';
int carry = 0, sum = 0, n = a.size();
string ans;
for(int i = 0; i < n; i++)
{
sum = carry + a[i] - '0' + b[i] - '0';
carry = sum / 10;
sum %= 10;
ans += '0' + sum;
}
if(carry) ans += '1';
reverse(ans.begin(), ans.end());
return ans;
}
减法:
string sub(string& a, string& b, bool& isNegtive)
{
if(a.size() < b.size() || (a.size() == b.size() && a < b))
{
a.swap(b);
isNegtive = true;
}
if(a == b)
{
return "0";
}
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
while(b.size() < a.size()) b += '0';
int n = a.size(), sub = 0, borrow = 0;
string ans;
for(int i = 0; i < n; i++)
{
sub = a[i] - b[i] - borrow;
if(sub >= 0) ans += sub + '0';
else
{
ans += sub + 10 + '0';
borrow = 1;
}
}
while(!ans.empty() && ans.back() == '0') ans.pop_back();
reverse(ans.begin(), ans.end());
return ans;
}
乘法 43. 字符串相乘:
class Solution {
private:
//a和b已经是倒序,返回值也是倒序: 123 -> "321",
string add(string& a, string& b)
{
if(a == "0") return b;
else if(b == "0") return a;
if(a.size() < b.size()) a.swap(b);
while(b.size() < a.size()) b += '0';
string ans;
int n = a.size(), sum = 0, carry = 0;
for(int i = 0; i < n; i++)
{
sum = a[i] - '0' + b[i] - '0' + carry;
carry = sum / 10;
sum %= 10;
ans += sum + '0';
}
if(carry) ans += '1';
return ans;
}
//a已经是倒序,返回值也是倒序
string multi(string& a, int m)
{
if(m == 0) return "0";
string ans;
int product = 0, carry = 0, n = a.size();
for(int i = 0; i < n; i++)
{
product = (a[i] - '0') * m + carry;
carry = product / 10;
product %= 10;
ans += product + '0';
}
if(carry) ans += to_string(carry);
return ans;
}
public:
string multiply(string num1, string num2) {
if(num1 == "0" || num2 == "0") return "0";
reverse(num1.begin(), num1.end());
reverse(num2.begin(), num2.end());
string ans;
for(int i = 0; i < num2.size(); i++)
{
string tmp = multi(num1, num2[i] - '0');
tmp = string(i, '0') + tmp;
ans = add(ans, tmp);
}
reverse(ans.begin(), ans.end());
return ans;
}
};
https://www.nowcoder.com/practice/ebe941260f8c4210aa8c17e99cbc663btpId=37&&tqId=21292&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking
矩阵乘法公式:对于 x * y 矩阵A 和 y*z矩阵B,它们的乘积C矩阵是 x * z矩阵,每个元素按下式计算:
#include
using namespace std;
vector> helper(const vector>& mat1, const vector>& mat2, int x, int y, int z)
{
vector> ans(x, vector(z, 0));
for(int i = 0; i < x; i++)
{
for(int j = 0; j < z; j++)
{
for(int k = 0; k < y; k++)
{
ans[i][j] += mat1[i][k] * mat2[k][j];
}
}
}
return ans;
}
int main(){
int x, y, z;
while(cin >> x >> y >> z)
{
vector> mat1(x, vector(y));
vector> mat2(y, vector(z));
for(int i = 0; i < x; i++)
{
for(int j = 0; j < y; j++)
{
scanf("%d", &mat1[i][j]);
}
}
for(int i = 0; i < y; i++)
{
for(int k = 0; k < z; k++)
{
scanf("%d", &mat2[i][k]);
}
}
vector> ans = helper(mat1, mat2, x, y, z);
for(const auto& row : ans)
{
for(int i = 0; i < row.size() - 1; i++)
{
printf("%d ", row[i]);
}
printf("%d\n", row.back());
}
}
return 0;
}
https://www.nowcoder.com/practice/cd99fbc6154d4074b4da0e74224a1582tpId=37&&tqId=21272&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking
多线程编程
4个数能否算出24点:每次取两个数,再放回数组, leetcode 原题 679. 24 点游戏
class Solution {
private:
bool equal0(double a)
{
return abs(a) < 1e-9;
}
bool equal24(double a)
{
return abs(a - 24) < 1e-9;
}
bool helper(const vector& nums)
{
if(nums.size() == 2)
{
double a = nums[0], b = nums[1];
return (equal24(a+b) || equal24(a-b) || equal24(b-a) || equal24(a*b) || (!equal0(b) && equal24(a/b)) || (!equal0(a) && equal24(b/a)));
}
for(int i = 0; i < nums.size(); i++)
{
for(int j = 0; j < nums.size(); j++)
{
if(i == j) continue;
vector tmp;
for(int k = 0; k < nums.size(); k++)
{
if(k != i && k != j) tmp.push_back(nums[k]);
}
tmp.push_back(nums[i] + nums[j]);
if(helper(tmp)) return true;
tmp.pop_back();
tmp.push_back(nums[i] - nums[j]);
if(helper(tmp)) return true;
tmp.pop_back();
tmp.push_back(nums[i]*nums[j]);
if(helper(tmp)) return true;
tmp.pop_back();
if(!equal0(nums[j]))
{
tmp.push_back(nums[i] / nums[j]);
if(helper(tmp)) return true;
tmp.pop_back();
}
}
}
return false;
}
public:
bool judgePoint24(vector& nums) {
vector t(nums.begin(), nums.end());
return helper(t);
}
};
附上输出所有可能的解的程序:
#include
using namespace std;
double eps = 1e-9;
bool equals24(double x){return abs(x-24) < eps;}
bool equals0(double x) {return abs(x) < eps;}
struct Node
{
double v;
string s;
Node(double v_ = 0): v(v_), s(to_string(static_cast(v_))){};
Node(double v_ , string s_ ): v(v_), s(s_){};
};
unordered_set ans;
void helper(const vector& nums)
{
if(nums.size() == 1)
{
if(equals24(nums[0].v)) ans.emplace(nums[0].s);
return;
}
for(int i = 0; i < nums.size(); i++)
{
for(int j = 0; j < nums.size(); j++)
{
if(i == j) continue;
vector next;
for(int k = 0; k < nums.size(); k++)
{
if(k != i && k != j) next.emplace_back(nums[k]);
}
double v = nums[i].v + nums[j].v;
string s = nums[i].s + "+" + nums[j].s;
next.emplace_back(Node(v, s));
helper(next);
next.pop_back();
v = nums[i].v - nums[j].v;
s = "";
s += nums[i].s + "-";
if(nums[j].s.find('+') != string::npos)
s += "(" + nums[j].s + ")";
else s += nums[j].s;
next.emplace_back(Node(v, s));
helper(next);
next.pop_back();
v = nums[i].v * nums[j].v;
s = "";
if(nums[i].s.find('+') != string::npos || nums[i].s.find('-') != string::npos)
s += "(" + nums[i].s + ")*";
else s += nums[i].s + "*";
if(nums[j].s.find('+') != string::npos || nums[j].s.find('-') != string::npos)
s += "(" + nums[j].s + ")";
else s += nums[j].s;
next.emplace_back(Node(v, s));
helper(next);
next.pop_back();
if(!equals0(nums[j].v))
{
v = nums[i].v / nums[j].v;
s = "";
if(nums[i].s.find('+') != string::npos || nums[i].s.find('-') != string::npos)
s += "(" + nums[i].s + ")/";
else s += nums[i].s + "/";
if(nums[j].s.find('+') != string::npos || nums[j].s.find('-') != string::npos)
s += "(" + nums[j].s + ")";
else s += nums[j].s;
next.emplace_back(Node(v, s));
helper(next);
next.pop_back();
}
}
}
}
int main(void) {
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
vector nums{Node(a), Node(b), Node(c), Node(d)};
helper(nums);
int idx = 1;
for(const auto& s : ans) cout << idx++ << ": " << s << endl;
return 0;
}
点分十进制字符串和十进制数之间相互转换。
https://www.nowcoder.com/practice/66ca0e28f90c42a196afd78cc9c496eatpId=37&&tqId=21256&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking
值得借鉴的做法:位运算
#include
#include
#include
using namespace std;
int main()
{
unsigned int a,b,c,d;
char ch;
while(cin>>a>>ch>>b>>ch>>c>>ch>>d)
{
cout<<((a<<24)|(b<<16)|(c<<8)|d)<>a;
cout<<((a&0xff000000)>>24)<<"."<<((a & 0x00ff0000)>>16)<<"."<<((a&0x0000ff00)>>8)<<"." <<(a & 0x000000ff)<
https://www.nowcoder.com/practice/6d9d69e3898f45169a441632b325c7b4tpId=37&&tqId=21247&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking
计算最少出列多少位同学,使得剩下的同学排成合唱队形
说明:
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足存在i(1<=i<=K)使得T1Ti+1>......>TK。 你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
最长递增子序列 + 最长递减子序列的问题:
如果以第 i 名同学做峰值,那么需要考察以他结尾的最长递增子序列的长度,作为左半部分;以他开始的最长递减子序列的长度,作为右半部分。最后计算选择哪一名同学做峰值能使留下的人最多即可。
#include
using namespace std;
void helper(const int n, const vector& queue)
{
vector lis(n, 1);
vector lds(n, 1);
for(int i = 0; i < n; i++)
{
for(int j = 0; j < i; j++)
{
if(queue[i] > queue[j]) lis[i] = max(lis[i], lis[j] + 1);
}
}
for(int i = n - 1; i >= 0; i--)
{
for(int j = i + 1; j < n; j++)
{
if(queue[i] > queue[j]) lds[i] = max(lds[i], lds[j] + 1);
}
}
int maxsize = 0;
for(int i = 0; i < n; i++) maxsize = max(maxsize, lis[i] + lds[i] - 1);
printf("%d\n", n - maxsize);
}
int main(){
int n;
while(cin >> n)
{
vector queue(n);
for(int i = 0;i < n; i++) scanf("%d", &queue[i]);
helper(n, queue);
}
return 0;
}
https://www.nowcoder.com/practice/f9c6f980eeec43ef85be20755ddbeaf4tpId=37&&tqId=21239&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking
有依赖关系的01背包问题。需要先把有依赖关系的所有可能组合列举出来,对于同一个被依赖对象,在这些组合中只能选1个最优的。
#include
using namespace std;
int N, m;
void helper(const vector>>& all)
{
m = all.size();
vector> dp(m+1, vector(N+1, 0));
//dp[i][j]: 从all[0...i-1]中选,和不超过j的最大价值
for(int i = 1; i <= m; i++)
{
for(int j = 0; j <= N; j++)
{
dp[i][j] = dp[i-1][j];
for(const auto& p : all[i-1])
{
if(j >= p.first) dp[i][j] = max(dp[i][j], dp[i-1][j-p.first] + p.second);
}
}
}
printf("%d\n", dp[m][N]);
}
int main()
{
while(cin >> N >> m)
{
int vd, pd, qd;
N /= 10;
vector>> all;
unordered_map v, p;
unordered_map> q;
unordered_set fujian;
for(int i = 0; i < m; i++)
{
scanf("%d%d%d", &vd, &pd, &qd);
v[i] = vd / 10;
p[i] = vd * pd;
if(qd > 0)
{
fujian.insert(i);
q[--qd].push_back(i);
}
}
for(int i = 0; i < m; i++)
{
if(fujian.count(i)) continue;
if(!q.count(i))
{
vector> tmp;
tmp.push_back({v[i], p[i]});
all.emplace_back(tmp);
continue;
}
if(q[i].size() == 1)
{
vector> tmp;
tmp.push_back({v[i], p[i]});
tmp.push_back({v[i] + v[q[i].front()], p[i] + p[q[i].front()]});
all.emplace_back(tmp);
}
else
{
vector> tmp;
tmp.push_back({v[i], p[i]});
tmp.push_back({v[i] + v[q[i].front()], p[i] + p[q[i].front()]});
tmp.push_back({v[i] + v[q[i].back()], p[i] + p[q[i].back()]});
tmp.push_back({v[i] + v[q[i].front()] + v[q[i].back()], p[i] + p[q[i].front()] + p[q[i].back()]});
all.emplace_back(tmp);
}
}
helper(all);
}
}