CUMTOJ算法实验一

问题 A: 判断字符串是否是手机号码

题目描述

手机号码是一串数字,长度为11位,并且第一位必须是1,现在给出一个字符串,我们需要判断这个字符串是否符合手机格式。

输入

多组测试数据。每组数据输入一个字符串。

输出

若该字符串符合手机号码格式,输出1,否则输出0。

样例输入

12345612345

样例输出

1

思路

可封装在一个返回 bool 的 Judge() 函数中,考虑三个方面:

  1. 是不是 11 位;
  2. 首位是不是为 “1”;
  3. 每位是不是数字。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#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)。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#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) << 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 取余所以可写出递推式:

1
Fib_seq[n] = (Fib_seq[n-1] + Fib_seq[n-2]) % mod;

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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

思路

函数的凹凸性

  1. 设函数f(x)在区间[a,b]上连续,(a,b)内可导,
    如果在区间(a,b)上f(x)的一阶导数单调递增,那么f(x)在[a,b]上是凹的。
    如果在区间(a,b)上f(x)的一阶导数单调递减,那么f(x)在[a,b]上是凸的。
  2. 设函数f(x)在区间[a,b]上连续,(a,b)内二阶可导,
    如果在区间(a,b)上f(x)的二阶导数>0,那么f(x)在[a,b]上是凸函数(图像为凹的)
    如果在区间(a,b)上f(x)的二阶导数<0,那么f(x)在[a,b]上是凹函数(图像为凸的)

三分法

  1. 三分法主要用于求解一个函数在某个区间内的极大(极小)值点,类似于二分法做一个比较:
二分法 三分法
作用 求解一个函数的零点 求解一个函数的极大(极小)值点
条件 函数在这个区间是单调函数 函数在这个区间是凸(凹)函数

对于一个凹函数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就是所求的极小值点。(可接受的误差内)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#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

思路

二分找到当前位置的前后。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#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


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以邮件至 xingshuaikun@163.com。

×

喜欢就点赞,疼爱就打赏