整理一下牛客网上的我觉得值得mark的题目,代码都是cpp.
1.求最小公倍数
本题实际上是求最大公约数。
如何求最大公约数? 辗转相除法。
m
a
x
d
i
f
(
a
,
b
)
=
{
a
b
=
0
m
a
x
d
i
f
(
b
,
a
%
b
)
o
t
h
e
r
maxdif(a,b)= \begin{cases} a & b = 0 \\ maxdif(b,a\%b) & other \end{cases}
maxdif(a,b)={amaxdif(b,a%b)b=0other
正整数A和正整数B 的最小公倍数是指 能被A和B整除的最小的正整数值,设计一个算法,求输入A和B的最小公倍数。
#include<iostream>
using namespace std;
int max_dif(int a, int b) //辗转相除法
{
while(b != 0)
{
int tmp = a % b;
a = b;
b = tmp;
}
return a;
}
int main()
{
int a,b;
while(cin >> a >> b)
{
int maxdif = max_dif(a,b);
// cout << maxdif;
int result = (a / maxdif) * maxdif * (b / maxdif);
cout << result << endl;
}
}
2.求一个数的立方根
设输入数为a
1.二分法,在0 - a直接二分查找即可,但是效率太慢。code就不写了。。。
2.牛顿迭代法 (具体证明不要问我,我也不会 ﹁ ﹁ )
f
(
x
)
=
x
3
−
a
f(x) = x^3 - a
f(x)=x3−a
f
(
x
)
=
f
(
x
0
)
+
(
x
−
x
0
)
f
′
(
x
0
)
=
0
f(x) = f(x_0) + (x - x_0)f'(x_0) = 0
f(x)=f(x0)+(x−x0)f′(x0)=0
x
−
x
0
=
−
f
(
x
0
)
f
′
(
x
0
)
x - x_0 = \frac{-f(x_0)}{f'(x_0)}
x−x0=f′(x0)−f(x0)
x
=
x
0
−
f
(
x
0
)
/
f
′
(
x
0
)
x = x_0 - f(x_0)/f'(x_0)
x=x0−f(x0)/f′(x0)
x
k
+
1
=
x
k
−
f
(
x
k
)
/
f
′
(
x
k
)
x_{k + 1} = x_k -f(x_k)/f'(x_k)
xk+1=xk−f(xk)/f′(xk)
当
x
k
x_k
xk与
x
k
+
1
x_{k+1}
xk+1接近时,
x
k
x_k
xk即为所求的立方根
#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;
/*
fx = x^3 - a = 0
fx = fx_0 + (x - x_0)^f'(x_0) = 0
x - x_0 = - fx_0 / f'(x_0)
x = x_0 - fx_0 / f'(x_0)
*/
double sqrt_3(double a);
int main()
{
double n;
while(cin >> n)
{
double result = sqrt_3(n);
cout << fixed << setprecision(1) << result << endl;
}
return 0;
}
double sqrt_3(double a)
{
double n = 1;
double n1 = n - (n * n * n - a)/ (3 * n * n);
while(abs(n - n1) > 1e-5)
{
n = n1;
n1 = n - (n * n * n - a)/ (3 * n * n);
}
return n1;
3.Redraiment的走法
Redraiment是走梅花桩的高手。Redraiment总是起点不限,从前到后,往高的桩子走,但走的步数最多,不知道为什么?你能替Redraiment研究他最多走的步数吗?
实际上就是求最长的严格递增子序列。
利用动态规划的思想可做。
d
p
[
j
]
=
m
a
x
(
d
[
i
]
)
+
1
0
<
=
i
<
j
dp[j] = max(d[i]) + 1 \qquad 0<= i < j
dp[j]=max(d[i])+10<=i<j
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
int main()
{
int n;
while( cin >> n)
{
vector<int> arr;
for(int i = 0 ; i < n;i++)
{
int tmp;
cin >> tmp;
arr.push_back(tmp);
}
vector<int> result;
result.resize(n);//result[i]代表末尾为第i位的最长递增子序列长度
int max_step = 0;
for(int i = 0;i < n;i++)
{
result[i] = 1;
for(int j = 0; j < i;j++)
{
if(arr[j] < arr[i])
result[i] = max(result[i],result[j] + 1);
}
max_step = max(max_step,result[i]);
}
cout<<max_step<<endl;
}
}
4.自守数
自守数是指一个数的平方的尾数等于该数自身的自然数。例如:25^2 = 625,76^2 = 5776,9376^2 = 87909376。请求出n以内的自守数的个数。
看了牛客网上90%的题解都是用长整形数据存储,很无语。。。
N-自守数
#include <stdio.h>
int main()
{
int M, N, K, L, mask;
scanf("%d", &M);
for(int i = 0; i < M; i++)
{
scanf("%d", &K);
for(N = 1; N < 10; N++)
{
L = N * K * K;
/* compare the lowest digits one by one */
for(mask = 1; mask <= K; mask *= 10)
if(L / mask % 10 != K / mask % 10)
break;
if(mask > K) /* all passed! */
{
printf("%d %d\n", N, L);
break;
}
}
if(N == 10) /* No results */
printf("No\n");
}
return 0;
}
5.埃及分数
分子为1的分数称为埃及分数。现输入一个真分数(分子比分母小的分数,叫做真分数),请将该分数分解为埃及分数。如:8/11 = 1/2+1/5+1/55+1/110。
埃及分数存在多种解法,这里只能保证生成的分数组合最短。
贪心算法
/*
a / b ( a < b)
b = a * c + d d:[0,c)
b / a = c + d/c < c + 1
a / b > 1 / (c + 1) (最大埃及分数为1/c + 1)
a / b - 1 / (c + 1) = a(c + 1) - b/ b ( c + 1)
*/
#include<iostream>
#include<vector>
using namespace std;
int max_div(int a, int b)
{
int tmp = 0;
while(b != 0)
{
tmp = a % b;
a = b;
b = tmp;
}
return a;
}
int main()
{
int a,b;
char ch;
while(cin >> a >> ch >> b)
{
//cout << a << b << endl;
int count = 0;
do{
int e = b / a + 1;
if(count != 0)
cout <<"+";
cout << "1/" << e;
a = a * e - b;
b = b * e;
int maxd = max_div(a,b);
if(maxd > 1)
{
b = b / maxd;
a = a / maxd;
} //不影响算法正确性,但是能保证生成的埃及分数序列最小。
count++;
} while(a > 1);
cout <<"+1/" << b << endl;
}
return 0;
}
6. 24点算法
问题描述:给出4个1-10的数字,通过加减乘除,得到数字为24就算胜利
输入:
4个1-10的数字。[数字允许重复,但每个数字仅允许使用一次,测试用例保证无异常数字]
输出:
true or false
经典回溯法的问题。
先从4个数中任取两个数计算结果,更新在某个数据上
然后以此类推,最后只剩一个数的时候判断是否 = 24;
详细的思路讲解去leetcode上看。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
bool find24points(vector<double> c);
int main()
{
vector<double> nums;
nums.resize(4);
while(cin >> nums[0] >> nums[1] >> nums[2] >> nums[3])
{
if(find24points(nums))
cout<<"true"<<endl;
else cout<<"false"<<endl;
}
}
bool find24points(vector<double> cur_num)
{
int len = cur_num.size();
if(len == 1)
{
return abs(cur_num[0] - 24) <= 1e-5 ?true:false;
}
else
{
for(int i = 0;i < len;i++)
{
for(int j = 0;j < len;j++)
{
if(j == i)
continue;
vector<double> new_arr;
for(int k = 0; k < len ;k++)
if(k != i && k != j)
{
new_arr.push_back(cur_num[k]);
}
else continue;
double t1 = cur_num[i] + cur_num[j];
new_arr.push_back(t1);
if(find24points(new_arr))
return true;
new_arr.pop_back();
double t2 = cur_num[i] - cur_num[j];
new_arr.push_back(t2);
if(find24points(new_arr))
return true;
new_arr.pop_back();
double t3 = cur_num[i] * cur_num[j];
new_arr.push_back(t3);
if(find24points(new_arr))
return true;
new_arr.pop_back();
if(cur_num[j] == 0)
continue;
double t4 = cur_num[i] / cur_num[j];
new_arr.push_back(t4);
if(find24points(new_arr))
return true;
new_arr.pop_back();
}
}
return false;
}
}
7. 查找输入二进制中整数1的个数
#include
using namespace std;
// 需要考虑负数,所以不能讲输入数字右移。
int main()
{
int n;
while(cin >> n)
{
int count = 0;
int flag = 1;
while(flag != 0)
{
//cout << flag << " " << count << endl;
if(flag & n) // 注意& ,<<的优先级,他们优先级是最低的
count++;
flag = flag << 1;
}
cout<<count<<endl;
}
}
或者使用
//把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0,那么一个整数的二进制表示中有
多少个1,就可以进行多少次这样的操作。
while(n != 0
{
n = ( n - 1) & n;
count ++;
}
cout << count
8.放苹果问题
题目描述
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
0 <= m <= 10 1 <= n <= 10
状态转移方程
C
(
m
,
n
)
=
{
1
m
=
0
∣
∣
n
=
1
C
(
m
,
n
−
1
)
+
C
(
m
−
n
,
n
)
m
>
=
n
C(m,n) = \begin{cases}1 & m = 0 || n = 1\\C(m,n - 1) + C(m - n,n) & m >= n \\ \end{cases}
C(m,n)={1C(m,n−1)+C(m−n,n)m=0∣∣n=1m>=n
大概解释一下就是苹果放盘子,要么存在空盘,要么不存在空盘。
#include<iostream>
#include<vector>
using namespace std;
// m [0,10] n[1,10]
int main()
{
int m,n;
while(cin >> m >> n)
{
vector<vector<int>> dp;
dp.resize(m + 1);
for(int i = 0;i < m + 1;i++)
dp[i].resize(n + 1);
for(int i = 0; i < m + 1;i++)
dp[i][1] = 1;
for(int i = 1; i < n + 1;i++)
{
dp[1][i] = 1;
dp[0][i] = 1;
}
for(int i = 2; i < m + 1 ;i++)
{
for(int j = 2; j < n + 1;j++)
{
if(i >= j)
dp[i][j] = dp[i][j - 1] + dp[i - j][j];
else dp[i][j] = dp[i][j - 1];
}
}
cout << dp[m][n] << endl;
}
}
9.查找字符串中第一个只出现一次的字符。
遍历字符串,对字符串中的每一个字符都同时进行从左边查找以及右边开始查找,若下标一样则返回当前下标为出现的第一个字符。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
// rfind 从字符串末尾开始查找
int main()
{
string s;
while(getline(cin , s))
{
string new_str;
vector<int> char_set(26,0);
int i = 0;
for(; i < s.length() ; i++)
{
if(s.find(s[i]) != s.rfind(s[i])) //左向查找and右向查找。
continue;
else {
break;
}
}
if(i == s.length())
cout << - 1 << endl;
else cout << s[i] << endl;
}
return 0;
}
10.合唱队
计算最少出列多少位同学,使得剩下的同学排成合唱队形
说明:
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足存在i(1<=i<=K)使得T1<T2<…<Ti-1Ti+1>…>TK。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
本题目实质上还是求最长递增子序列的问题。
需要计算每位同学在以他为队尾的时候最长递增子序列的长度。
#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int n;
while(cin >> n)
{
vector<int> arr;
for(int i = 0; i < n;i++)
{
int tmp;
cin >> tmp;
arr.push_back(tmp);
}
vector<int> up_arr;
up_arr.resize(n);
for(int i = 0; i < n ;i++)
{
up_arr[i] = 1;
for(int j = 0; j < i;j++)
{
if(arr[j] < arr[i])
up_arr[i] = max(up_arr[i],up_arr[j] + 1);
}
}
vector<int> down_arr;
down_arr.resize(n);
for(int i = n - 1; i >= 0;i--)
{
down_arr[i] = 1;
for(int j = n - 1;j > i;j--)
if(arr[j] < arr[i])
down_arr[i] = max(down_arr[i],down_arr[j] + 1);
}
int max_K = 0;
for(int i = 0 ; i < n;i++)
{
up_arr[i] += down_arr[i] - 1;
max_K = max(up_arr[i],max_K);
}
cout<< n - max_K <<endl;
}
return 0;
}