“战疫杯”在线邀请赛——第五场题解

“战疫杯”在线邀请赛——第五场题解

题目详情 - 1 感染源在哪里 (pintia.cn)

通过最近的一次核酸检测,疫情防控小组检测到了若干个阳性人员,通过调取行程码数据,防控小组获取到了这些病例曾经去过的地点。现在希望对这些地点做源头可能性分析,来找出哪些地点更有可能是本次疫情的源头。

对于一个病例,如果其曾到过a,b,c三个地点,那么这三个地点都会在其源头可能得分上增加1/3,如果某个病例曾经去过a,c两个地点,那么这两个地点作为源头的得分都会增加1/2,如果只去过一个地点的病例,那么该地点作为源头的概率很高,我们给这个地点的得分增加1。

简而言之,如果一个病例去过n个地点,那么这n个地点都会得到1/n的得分。现在给你所有病例去过的地点的情况,你能否分析出每个地点作为源头的可能性大小顺序?

输入格式:

第一行一个整数n(1≤n≤1000),表示记录的条数
其后n行,每行两个不为空的字符串a,b(1≤∣a∣,∣b∣≤10),仅包含小写字母。其中a代表病例的名字,b代表地点。

注意:当出现有人多次去同一地点时,只计算一次

输出格式:

输出若干行,每行代表一个地点名,代表所有地点作为源头的得分从高到低排序的结果。如果有多个地点得分相同,地点名字典序小的优先。

输入样例:

6
xiaoa adian
xiaob adian
xiaob bdian
xiaoc adian
xiaoc bdian
xiaoc cdian

输出样例:

adian
bdian
cdian

问题解析

(注:本题方法容易被hack)

这里的主要问题就是分配分数,如果一个人去了n个地方,那么这个地方会得到1/n的分数,本人试过用double来算每个地方的分数但是wa了一发,然后一气之下换了个非常粗暴危险的方法。

假设此题一共有m个不同的地方,那么一个人最多只会去到m个地方,可能分配分数的方式就是1/1、1/2、1/3……1/m。我直接用一个longlong变量ans来当总分,我们不一定要算的是1/m,可以是ans/m,也就是说对于一个人去到地点数的不同,我们可以分成ans/1,ans/2,ans/2,……,ans/m。那么只要这个ans可以被1……m的数整除,这问题就会变得非常简单了。

我用一个map容器res来记录不同地点的数量,然后用ans=1从1一直乘到res.size()为止,这样ans必然会被1……res.size()的任意数整除。用mymap来记录每个人走过的地方的情况,cnt是防止重复记录一个人去的地方(比如a去了两次bdian,我们只记录一次,第二次就不记录了)。

然后我们根据每个人去的地点来分配分数,再根据地点的分数进行排序后输出。

(longlong最多可以存的数大概是1e19这样,也就是说要是有20个不同的地方我这个方法就寄了)

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 3e5 + 50;

bool cmp(pair<ll, string>& a, pair<ll, string>& b)
{
    if (a.first != b.first)return a.first > b.first;
    return a.second < b.second;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    string s1, s2;
    cin >> n;
    map<string, vector<string>>mymap;
    map<string, map<string, int>>cnt;
    map<string, ll>res;
    for (int i = 0; i < n; i++)
    {
        cin >> s1 >> s2;
        if (cnt[s1][s2] != 0)continue;
        mymap[s1].push_back(s2);
        cnt[s1][s2] = 1;
        res[s2] = 0;
    }
    ll ans = 1;
    for (int i = 1; i <= res.size(); i++)
    {
        ans *= i;
    }
    for (auto i : mymap)
    {
        int len = i.second.size();
        for (auto j : i.second)
        {
            res[j] += ans / len;
        }
    }
    vector<pair<ll, string>>v;
    for (auto i : res)
    {
        v.push_back({ i.second,i.first });
    }
    sort(v.begin(), v.end(), cmp);
    int len = v.size();
    for (int i = 0; i < len; i++)
    {
        cout << v[i].second;
        if (i != n - 1)cout << endl;
    }
    return 0;
}

题目详情 - 2 病毒序列 (pintia.cn)

时至至今,疫情还没结束,战斗并未停止!现如今,疫情肆虐全球,疫情的传播加速了病毒的变异,α病毒,β病毒,δ病毒,相继出现…

而小A对病毒的变异来了兴趣,带着高中学过的生物知识,小A了解到病毒可以表示为由A,U,C,G四种碱基组成的基因序列,而病毒的变异便来自于不同基因序列结合。在此,我们将两个不同的基因序列的结合定义为:两个基因序列上下排布,通过一定的错位,使得两个基因序列的部分碱基可以对应起来,若分属两个基因序列的A与U对应则形成3个氢键,若分属两个序列的C与G对应会形成2个氢键,如图所示:

QQ截图20220513155014.png

顽固的病毒为了保证自己结构的稳定性,在变异过程中总会选择形成氢键个数最大的方式进行结合。现在给定两个基因序列,他们结合后的氢键个数是多少?

PS:题目描述的“结合”仅仅为本题目描述所定义,不一定符合真实的生物学性质。

输入格式:

两行,每行一个字符串,分别是两个待结合的基因序列。(保证字符串只有A,U,C,G组成,且字符串的长度小于等于5000).

输出格式:

一行一个整数,表示结合后的最大氢键个数。

输入样例:

AGC
UUCG

输出样例:

7

样例解释

共有6种可能的结合方式

AGC
UUCG

AGC
 UUCG
    
AGC
  UUCG
         
 AGC
UUCG

  AGC
UUCG

   AGC
UUCG

其中形成氢键的个数分别为:3,0,0,7,0,0
则病毒序列会选择氢键个数为7的方式进行结合

问题解析

这个样例解释的很清楚,就是说两个字符串以不同位置开始匹配,然后看从哪个位置开始匹配分数最高。

拿样例来说,我们可以这么匹配:

   AGC  ————>s1
UUCG  ————>s2

  AGC
UUCG

 AGC
UUCG

AGC
UUCG

AGC
 UUCG
    
AGC
  UUCG

我们可以通过如上流程,先从s2的屁股开始匹配,然后位置逐渐往前移,当s1的屁股到达s2的头部时,就是最后一步了,然后我们在此过程中维护最大的分数,最后输出分数即可。为了方便,我是在s2的尾部加上了相当于s1长度的无用字符,然后我们以尾巴对尾巴的方式来匹配,每次匹配完后去掉s2的尾部字符,当s2被删干净后,匹配结束,模拟一下就是:

   AGC  
UUCG**  

  AGC
UUCG*

 AGC
UUCG

AGC
UUC

AGC
 UU
    
AGC
  U

个人认为这样比较方便,当然要是有更适合自己的方法是更好。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 3e5 + 50;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    string s1, s2;
    cin >> s1 >> s2;
    int res = 0, ans = 0;
    for (int i = 0; i < s1.size(); i++)
        s2 += '*';
    int l = 0, r = 0;
    while (s2.size() != 0)
    {
        l = s1.size(), r = s2.size();
        ans = 0;
        while (l >= 0 && r >= 0)
        {
            if (s1[l] == 'A' && s2[r] == 'U')
            {
                ans += 3;
            }
            else if (s1[l] == 'U' && s2[r] == 'A')
            {
                ans += 3;
            }
            else if (s1[l] == 'C' && s2[r] == 'G')
            {
                ans += 2;
            }
            else if (s1[l] == 'G' && s2[r] == 'C')
            {
                ans += 2;
            }
            l--, r--;
        }
        res = max(res, ans);
        s2.pop_back();
    }
    cout << res;
    return 0;
}

题目详情 - 3 奇奇怪怪的形状 (pintia.cn)

小凡有一张边长为2的正方形白纸,上面画了2×2个边长为1的格子,每个格子上可以放置棱长为1的立方体。小凡手中有若干个棱长为1的立方体,他随机地在每个格子上放置一些立方体,求由这些立方体组成的几何体的表面积。

image.png

输入格式:

输入两行,每行两个不超过100的正整数,代表从俯视的角度看,白纸所画每个格子上立方体的数量。

输出格式:

输出一行,包含一个整数,表示几何体的表面积。

输入样例:

上图对应的输入数据如下:

2 1
1 1

输出样例:

20

问题解析:

这题其实相当暖心了,由于条件限制这题就显得很简单。

首先题目确保了这个摆出的物体底部是2*2的。

我们根据输入的数字用变量abcd来接收:

a:2  b:1
c:1  d:1

表面积就是漏在外面能直接被我们看到部分的面积,然后我们看向它的6个方向:上下左右前后.

首先由于它底部是固定2*2,那么不管怎么样,从上往下看和从下往上看能看到的图形都是一个2 *2的正方形,所以底部的表面积和顶部的表面积都是4,现在表面积是8。

然后我们从前往后看,看到的图形就是:
在这里插入图片描述

表面积为:3,现在表面积是11.

从后往前看得到的图形其实和从后往前看是一样的(只是反过来):
在这里插入图片描述

表面积依然为3:现在表面积是14.

从左往右看得到的图形是:
在这里插入图片描述

表面积为:3,现在表面积是17.

从右往左看到的图形和从左往右看到的图形是相反的:
在这里插入图片描述

表面积为:3,现在表面积是20.

所以算表面积,顶部和底部的4*2点已经固定了,至于前后左右看取决于它摆放的高度如何。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 3e5 + 50;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    int res = 8;
    //这是从前往后和从后往前得到的面积
    res += (max(a, c) + max(b, d))*2;
    //这是从左往右和从右往左得到的面积
    res += (max(d, c) + max(a, b))*2;
    cout << res;
    return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值