写在前面
训练日期:2021.07.19
训练Result: 赛时AC 4题
动力源泉:你想要的东西很贵,你想去的地方很远。
索引导航
Problem A: 汽水瓶
评测传送门
题目详情:
解题思路:
这个题依据题目描述简单模拟即可,注意最后剩余两个瓶子的时候可以向老板借一个空瓶来凑足3个瓶子依然能换一瓶汽水。
AC coding:
#include <map>
#include <set>
#include <string>
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int main()
{
int n;
while (cin >> n) {
if (n == 0) break;
int sum = 0;
while (n >= 3) {
int tmp = n / 3;
sum += tmp;
n %= 3;
n += tmp;
}
if (n == 2) sum++;
cout << sum << endl;
}
return 0;
}
Problem B: 弟弟的作业
评测传送门
题目详情:
解题思路:
这个题是字符串处理的常规操做,只有两种运算,加法或减法;二者分开讨论,分别找到 “
+
+
+” 或 “
−
-
−”的位置,再结合“
=
=
=”的位置,对字符串进行分割,分离出加数,被加数,和(或减数,被减数,差),将字符型转换成整型,进行相应的运算,将运算结果再转回字符串型,比较运算结果和算式所给结果是否相同,相同即运算正确。
AC coding:
#include <map>
#include <set>
#include <string>
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int get(string a) {
int sum = 0;
for (int i = 0;i < a.size();i++) {
sum = sum * 10 + a[i] - '0';
}
return sum;
}
string trans(int x) {
int flag = false;
if (x < 0) flag = true;
x = abs(x);
string str = "";
while (x) {
char c = '0' + (x % 10);
str = c + str;
x /= 10;
}
if (flag) str = '-' + str;
return str;
}
int main()
{
int sum = 0;
string str;
while (cin >> str) {
bool flag = false;
for (int i = 0;i < str.size();i++) {
if (str[i] == '+') flag = true;
}
if (flag) {
int idx = str.find('+');
string a = str.substr(0, idx);
int idx2 = str.find('=');
string b = str.substr(idx + 1, idx2 - idx - 1);
int x = get(a);
int y = get(b);
int z = x + y;
string res = trans(z);
if (res == str.substr(idx2 + 1, str.size() - idx2)) sum++;
}
else {
int idx = str.find('-');
string a = str.substr(0, idx);
int idx2 = str.find('=');
string b = str.substr(idx + 1, idx2 - idx - 1);
int x = get(a);
int y = get(b);
int z = x - y;
string res = trans(z);
//cout << str.substr(idx2 + 1, str.size() - idx2) << endl;
if (res == str.substr(idx2 + 1, str.size() - idx2)) sum++;
}
}
cout << sum << endl;
return 0;
}
Problem C: 数字整除
题目详情:
解题思路:
这个题是大数运算的题目,如果用C++来写的话要用到高精度计算中的大数减法和大数除法(高精除低精)。根据余数是否为
0
0
0 来判断是否整除;
但是对于
p
y
t
h
o
n
python
python 来说,
p
y
t
h
o
n
python
python 支持大数运算,完全不需要模拟,直接计算即可。C++代码写出来相比于
p
y
t
h
o
n
python
python代码会稍复杂一点,简便起见,我在这里写了
p
y
t
h
o
n
python
python 版本的代码。
AC coding:
#coding:utf-8
while(True):
n = int(input())
if n==0:
break
g = n%10
n = n//10
if (n - (5 * g)) % 17 == 0:
print(1)
else:
print(0)
Problem D: 台球碰撞
评测传送门
题目详情:
解题思路:
运用物理上学到的知识,将台球运动的速度沿水平方向垂直方向分解,这样对于两个方向上的运动可做单独运算,互不影响。对于小球有半径如何处理,只需将原左面的上下左右都裁剪R的宽度。完全非弹性碰撞,碰撞前后无能量损失,表现为速度大小不变。
AC coding:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int main()
{
int l, w, x, y, R, a, v, s;
while (cin >> l >> w >> x >> y >> R >> a >> v >> s) {
if (l == 0 && l == w && l == x && l == y && l == R && l == a && l == v && l == s) {
break;
}
double L = l - R;
double W = w - R;//便于分析圆心所在位置
/*将角度转换成弧度*/
double alpha = PI * a / 180.0;
/*将速度沿水平方向和竖直方向分解*/
double Vx = v * cos(alpha);
double Vy = v * sin(alpha);
double X = Vx * s + x;
double Y = Vy * s + y;
while (X < R || X > L) {
if (X < R) X = R + (R - X);
if (X > L) X = L - (X - L);
}
while (Y < R || Y > W) {
if (Y < R) Y = R + (R - Y);
if (Y > W) Y = W - (Y - W);
}
printf("%.2lf %.2lf\n", X, Y);
}
return 0;
}
Problem E: 内部收益率
评测传送门
题目详情:
解题思路:
对于题目所给方程的左边的式子,将其看作一个函数,将IRR看作自变量,会发现随着IRR的增大,函数值减小,即左侧的这个函数单调递减。
C
R
0
<
0
CR_0 < 0
CR0<0,
C
R
i
>
0
(
i
>
=
1
)
CR_i > 0(i >= 1)
CRi>0(i>=1) 当IRR取值大于-1且很接近-1的一个值的时候,会发现分母 1 - IRR会变的非常小,分子大于0,则整体会是很大的一个正数,所以当T无穷大的时候函数一定存在唯一的零点。根据浮点数二分的方法二分出零点即可。不存在多解的情况。
AC coding:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int c[30];
int t;
double qpow(double x,int y){
double res = 1;
while(y--) res = res * x;
return res;
}
double get(double x){
double res = c[0];
for(int i = 1;i <= t;i++){
res += c[i]/qpow(1 + x,i);
}
return res;
}
int main(){
while(cin >> t){
if(t == 0) break;
for(int i = 0;i <= t;i++){
cin >> c[i];
}
double l = -1.0 + 1e-8,r = 1000000000000.0;
while(r - l > 1e-8){
double mid = (r + l) / 2.0;
if(get(mid) >= 0) l = mid;
else r = mid;
}
if(fabs(get(l)) < 1e-3) printf("%.2lf\n",l);
else printf("No\n");
}
return 0;
}
精度这玩意儿不好说,多试几遍就卡对了,哈哈哈。
Problem F: Biggest Number
题目详情:
题目大意:
可以从任意一个数字所在的位置出发,只能沿上下左右四个方向向前走,且只能走数字所在的格子,不能重复走某一个格子。求一条路径长度最长且在在同等路径长度下字典序最大的路径。
解题思路:
DFS+BFS强剪枝。DFS无疑是用来搜索路径的,为了搜索到有效的路径,需要用BFS来提前判断当前点是否还有搜索的必要。判断的关键点是搜索当前点还能走多远,当前搜索的距离加上还能走的距离大于目前所存储答案的长度,直接往下搜;若等于所存储答案的长度,则需将当前点到还能搜索到的最远的点的路径中的所有点存储起来,按逆序排序后和当前DFS搜索序列拼接后与当前所存储的答案的字典序相比较,大于的话,直接搜,否则,不搜索,回退;若小于所存储的答案的长度,直接回退。
AC coding:
#include <string>
#include <cstring>
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 100;
char g[N][N];
int n, m;
int dx[] = { 1,0,-1,0 };
int dy[] = { 0,1,0,-1 };
bool vis[N][N],Vis[N][N];
string res;
PII q[1000010];
bool ok(int x, int y) {
if (x >= 1 && x <= n && y >= 1 && y <= m) return true;
else return false;
}
string bfs(int x, int y) {
memset(Vis, false, sizeof Vis);
int hh = 0,tt = -1;
q[0] = { x,y };
tt++;
Vis[x][y] = true;
string str = "";
while (hh <= tt) {
PII tmp = q[hh++];
str += g[tmp.first][tmp.second];
for (int i = 0;i < 4;i++) {
int xx = tmp.first + dx[i];
int yy = tmp.second + dy[i];
if (ok(xx, yy) && isdigit(g[xx][yy]) && !Vis[xx][yy] && !vis[xx][yy]) {
Vis[xx][yy] = true;
q[++tt] = { xx,yy };
}
}
}
sort(str.begin(), str.end());
reverse(str.begin(), str.end());
return str;
}
void dfs(int x, int y, string sum) {
vis[x][y] = true;
sum += g[x][y];
if (sum.size() > res.size() || (sum.size() == res.size() && sum > res)) res = sum;
for (int i = 0;i < 4;i++) {
int xx = x + dx[i];
int yy = y + dy[i];
if (ok(xx, yy) && isdigit(g[xx][yy]) && !vis[xx][yy]) {
string str = bfs(xx, yy);
int maxlen = str.size();
if (sum.size() + maxlen > res.size() || (sum.size() + maxlen == res.size() && sum + str > res))
dfs(xx, yy,sum);
}
}
vis[x][y] = false;
}
int main()
{
while (cin >> n >> m) {
if (n == 0 && m == 0) break;
for (int i = 1;i <= n;i++) cin >> g[i] + 1;
res = "";
memset(vis, false, sizeof vis);
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
if(isdigit(g[i][j])) dfs(i, j, "");
}
}
cout << res << endl;
}
return 0;
}
Problem G: Repairing a Road
评测传送门
题目详情:
题目大意:
有
R
+
1
R + 1
R+1 个城市,给出一个城市
C
C
C 和
R
R
R 条路。
R
R
R条路连接这
R
+
1
R+1
R+1个城市,对于每条路有一个时间属性
v
v
v(通过该条路所需时间) 和特性值
a
a
a 。你要从城市
1
1
1 沿路走到城市
C
C
C,求所需的最短时间。期间可以请朋友来为某一条路修理快速通道,修理之后通过该条路的时间变为
v
∗
a
(
−
t
)
v*a^{(-t)}
v∗a(−t) ,其中
t
t
t 为修路所用时间。只能请朋友修理一次。朋友在修路期间,你无法走当前正在修的路。
解题思路:
首先观察到数据范围并不是很大,求从城市1到城市C的最短花费时间,我们可以直接使用
F
l
o
y
d
Floyd
Floyd 算法来处理。注意:经过floyd算法处理之后,只能得到不请朋友修路的前提下,从
1
1
1到
C
C
C 所需花费的最短时间。当我们请朋友帮忙修路时,我们可以暴力枚举所有的路,依次判断修理哪一条路才是最优的即可。
这里需要分析一下,修理后所需时间。
首先,修理时间是未知的,假设修理时间是
t
t
t ,通过当前路段时间加修理时间是
t
总
=
v
∗
a
(
−
t
)
+
t
t
总
′
=
1
−
v
∗
a
(
−
t
)
∗
l
n
(
a
)
t
总
′
′
=
v
∗
l
n
2
(
a
)
∗
a
(
−
t
)
>
0
t_总=v*a^{(-t)}+t \\ t_总^{'} = 1-v*a^{(-t)}*ln(a) \\ t_总^{''} = v*ln^2(a)*a^{(-t)} > 0
t总=v∗a(−t)+tt总′=1−v∗a(−t)∗ln(a)t总′′=v∗ln2(a)∗a(−t)>0
求出
t
总
t_总
t总 的最小值,根据二阶导可知一阶导函数是一个单调递增函数,
t
总
′
(
0
)
t_总^{'}(0)
t总′(0) 应该是一个小于0的数,当
a
>
=
e
a>=e
a>=e时,
t
总
′
(
0
)
<
0
t_总^{'}(0) < 0
t总′(0)<0恒成立,当
1
<
=
a
<
e
1 <= a < e
1<=a<e时,暂时我还不会证明。这里直接按照小于0处理的,这样的话,原函数
t
总
(
t
)
t_总(t)
t总(t)存在极小值,令一阶导等于
0
0
0 求出极值点,
t
=
l
o
g
a
(
l
n
(
a
)
∗
v
)
t=log_a(ln(a)*v)
t=loga(ln(a)∗v),通过换底公式可得,
t
=
l
n
(
l
n
(
a
)
∗
v
)
/
l
n
(
a
)
t=ln(ln(a)*v)/ln(a)
t=ln(ln(a)∗v)/ln(a).
若修理道路的信息为{x,y,v,a}。则
r
e
s
u
l
t
=
m
a
x
(
t
,
d
i
s
t
[
1
]
[
x
]
)
+
v
∗
a
(
−
t
)
+
d
i
s
t
[
y
]
[
C
]
.
result= max(t,dist[1][x]) + v*a^{(-t)} + dist[y][C].
result=max(t,dist[1][x])+v∗a(−t)+dist[y][C].
AC coding:
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 500;
struct edge {
int x, y;
double v, a;
}e[N];
double dist[N][N];
int C, n, R;
void init() {
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
if (i == j) dist[i][j] = 0;
else dist[i][j] = 10000000;
}
}
}
void floyd() {
for (int k = 1;k <= n;k++) {
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
int main()
{
while (cin >> C >> R) {
queue<edge> q;
if (C == 0 && R == 0) break;
n = R + 1;
init();
for (int i = 1;i <= R;i++) {
int x, y;
double v, a;
cin >> x >> y >> v >> a;
dist[x][y] = dist[y][x] = v;
e[i] = { x,y,v,a };
q.push({ x,y,v,a });
q.push({ y,x,v,a });
}
floyd();
/*for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
cout << dist[i][j] << " ";
}
cout << endl;
}*/
double res = dist[1][C];
while (q.size()) {
edge head = q.front();q.pop();
double t = 0.0;
if (fabs(head.a - 1) > 1e-6) {
t = log(head.v * log(head.a)) / log(head.a);
}
t = max(t, dist[1][head.x]);
res = min(res,t + head.v * pow(head.a, -t) + dist[head.y][C]);
}
printf("%.3lf\n", res);
}
return 0;
}
顿悟的不够,还未解出的一些题。
暂鸽。。。
Problem H: 射击游戏
Problem I: 战场的数目
Problem J: Infinite Dictionaries
Problem K: Tetrahedrons and Spheres