CUMTOJ算法实验一
问题 A: 判断字符串是否是手机号码
题目描述
手机号码是一串数字,长度为11位,并且第一位必须是1,现在给出一个字符串,我们需要判断这个字符串是否符合手机格式。
输入
多组测试数据。每组数据输入一个字符串。
输出
若该字符串符合手机号码格式,输出1,否则输出0。
样例输入
12345612345
样例输出
1
思路
可封装在一个返回 bool 的 Judge() 函数中,考虑三个方面:
- 是不是 11 位;
- 首位是不是为 “1”;
- 每位是不是数字。
代码
#include<iostream>
#include<string.h>
using namespace std;
const int num = 1000;
bool Judge(char str[]);
int main() {
char str[num];
while(cin >> str) {
if(Judge(str))
cout << "1" << endl;
else
cout << "0" << endl;
}
return 0;
}
bool Judge(char str[]) {
if(strlen(str) != 11 || str[0] != '1')
return false;
for(int i = 0; i < 11; i++) {
if(str[i] < '0' || str[i] > '9')
return false;
}
return true;
}
问题 B: 内部收益率
题目描述
输入
输出
对于每组数据,输出仅一行,即项目的IRR,四舍五入保留小数点后两位。
样例输入
1
-1 2
2
-8 6 9
0
样例输出
1.00
0.50
思路
题目两边等式同乘 (1+IRR)T。则原题相当于求:CF0(1+IRR)T+CF1(1+IRR)T−1+⋯+CFT=0 的解,则可使用二分法。
L 初始值取 -1.0,R 初始值取常规正无穷大(我这里取100000)。
代码
#include <iostream>
#include <iomanip>
#include <string.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int inf_num = 100000;
const double eps = 1e-6;
int main() {
int T;
double cf[13] , l , r , m;
while(cin >> T && T) {
for(int i = 0; i <= T; i++)
cin >> cf[i];
l = -1.00;
r = inf_num;
while(r - l > eps) {
m = (l + r) / 2;
double sum = 0.0;
for(int i = 0; i <= T; i++)
sum += cf[i] * pow(1 + m , T - i);
if(sum > 0)
l = m;
else
r = m;
}
cout << setiosflags(ios::fixed) << setprecision(2);
cout << l << endl;
}
return 0;
}
问题 C: 跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
输入
多组测试样例。每组测试样例包含一个整数n。(1<=n<=100)
输出
每组测试样例输出一行,表示青蛙跳上n级台阶的跳法数量.
所得到的结果模1000000007
样例输入
3
4
样例输出
3
5
思路
假设青蛙在第 n 级台阶,那么我们倒推一步,故易得青蛙跳台阶的问题就是斐波那契数列的问题
此题有时间限制,故用空间换时间,用一个数组存储结果
因为它每次可以跳 1 级台阶或者 2 级台阶,所以它上一步只能在第 n-1 级或者第 n-2 级台阶,每次都要对 1e9 + 7 取余所以可写出递推式:
Fib_seq[n] = (Fib_seq[n-1] + Fib_seq[n-2]) % mod;
代码
#include <iostream>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int arr_len = 101;
ll Fib_seq[arr_len] = {1 , 1};
int main() {
int step_num;
for(int i = 2; i < arr_len; i++)
Fib_seq[i] = (Fib_seq[i - 1] + Fib_seq[i - 2]) % mod;
while(cin >> step_num)
cout << Fib_seq[step_num] << endl;
return 0;
}
问题 D: 奶牛的聚会
题目描述
输入
输出
对于每组测试数据,输出 “Case #c: ans” ,其中c表示测试数据编号,ans表示消极情绪之和的最小值,结果四舍五入为一个整数。
样例输入
1
5
0.9 2
1.4 4
3.1 1
6.2 1
8.3 2
样例输出
Case #1: 300
思路
函数的凹凸性
- 设函数f(x)在区间[a,b]上连续,(a,b)内可导,
如果在区间(a,b)上f(x)的一阶导数单调递增,那么f(x)在[a,b]上是凹的。
如果在区间(a,b)上f(x)的一阶导数单调递减,那么f(x)在[a,b]上是凸的。 - 设函数f(x)在区间[a,b]上连续,(a,b)内二阶可导,
如果在区间(a,b)上f(x)的二阶导数>0,那么f(x)在[a,b]上是凸函数(图像为凹的)
如果在区间(a,b)上f(x)的二阶导数<0,那么f(x)在[a,b]上是凹函数(图像为凸的)
三分法
- 三分法主要用于求解一个函数在某个区间内的极大(极小)值点,类似于二分法做一个比较:
二分法 | 三分法 | |
---|---|---|
作用 | 求解一个函数的零点 | 求解一个函数的极大(极小)值点 |
条件 | 函数在这个区间是单调函数 | 函数在这个区间是凸(凹)函数 |
对于一个凹函数y=f(x),我们要求它的极小值点。
首先确定它的极小值点所在的区间为[l,r]
计算出两个三分点:
mid=(l+r)/2
mid2=(mid+r)/2
(其实这两个点的位置是灵活的)
此时 l < mid <mid2 < r
计算出对应的函数值 f(mid)和f(mid2)。
当f(mid)<f(mid2)时,则极小值点一定不会在mid2和r之间。
反之f(mid)>f(mid2)时,极小值点一定该不会在l和mid之间。
因此,当f(mid)<f(mid2)时,极小值点在[l,mid2]内。此时令l=l,r=mid2继续计算。
当f(mid)>f(mid2)时,极小值点在[mid,r]内。此时令l=mid,r=r继续计算。
直到这个区间足够小,可以认为l=r时,l就是所求的极小值点。(可接受的误差内)
代码
#include <iostream>
#include <cmath>
typedef long long ll;
using namespace std;
double x[50005][2];
int n;
double cal(double xx) {
double sum = 0;
for(int i = 0; i < n; i++)
sum += fabs(x[i][0] - xx) * fabs(x[i][0] - xx) * fabs(x[i][0] - xx) * x[i][1];
return sum;
}
int main() {
int t;
cin >> t;
for(int w = 1; w <= t; w++) {
cin >> n;
for(int i = 0; i < n; i++)
cin >> x[i][0] >> x[i][1];
double L = -1000000 , r = 1000000;
while(L + 0.00001 < r) {
double Lmid = L + (r - L) / 3;
double rmid = r - (r - L) / 3;
if(cal(Lmid) < cal(rmid))
r = rmid;
else
L = Lmid;
}
cout << "Case #" << w << ": " << (ll)(cal(L) + 0.5) << endl;
}
return 0;
}
问题 E: 光合作用
题目描述
蒜头是个爱学习的孩子,他总喜欢在生活中做一些小实验,这次蒜头想研究一下光合作用。蒜头的实验材料有如下几样:神奇的种子,普通的纸箱和一些光源。一开始,蒜头将种子均匀的种在了箱子底部,你可以将其看成 X 轴,种子的位置为 X 轴上的点。然后蒜头用纸板将箱子盖住,并在纸板上安装了一些光源(具体见图,顶上的为光源,光源两边与顶部的夹角都为45度,黄色部分为光照,绿色的为植物。)。神奇的种子会在有光的情况下一直向上生长直到没光为止。现在蒜头想知道当实验结束时每颗种子的高度是多少?
输入
第一行输入一个整数 T,表示测试数据的组数。
每组数据的第一行是三个整数 n,m,h(1<=n,m<=1e5,0<=m<=1e5,1<=h<=1e4),n表示种子数(编号为1,2…n),m表示光源数,h 表示箱子的高度。
接下来m行,每行一个整数Xi表示第i个光源在顶部的位置。
输出
对于每组测试数据,请输出n行,每行一个数表示第i颗种子的最终高度。
样例输入
2
7 1 2
4
4 4 1
1
2
3
4
样例输出
0
0
1
2
1
0
0
1
1
1
1
思路
二分找到当前位置的前后。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int max_num = 1e5 + 5;
int arr[max_num];
int main() {
int T;
cin >> T;
while(T--) {
int n , m , h;
cin >> n >> m >> h;
for(int i = 1; i <= m; i++)
cin >> arr[i];
sort(arr + 1 , arr + m + 1);
for(int i = 1; i <= n; i++) {
int ans = 0;
int cnt = lower_bound(arr + 1 , arr + m + 1 , i) - arr;
if(cnt == 1 && m != 0)
ans = max(ans , h - arr[cnt] + i);
else if(cnt == m + 1 && m != 0)
ans = max(ans , h - i + arr[cnt - 1]);
else if(m != 0)
ans = max(0 , max(h - i + arr[cnt - 1] , h - arr[cnt] + i));
cout << ans << endl;
}
}
return 0;
}
本文参考了如下大佬们的文章
https://comydream.github.io/2018/09/28/algorithm-experiment-1/
https://www.cnblogs.com/yfr2zaz/p/11083207.html
https://www.cnblogs.com/syiml/p/3675214.html
https://www.cnblogs.com/littlepear/p/6033783.html