【暑假训练1】2010年湖南省赛题解

写在前面

训练日期: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)} va(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=va(t)+tt=1va(t)ln(a)t=vln2(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])+va(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

H题解
I题解
J题解
K题解 ?????找不着。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值