Codeforces Round 916 (Div. 3)(G未补)

目录

A. Problemsolving Log

B. Preparing for the Contest

C. Quests

D. Three Activities

E1.E2. Game with Marbles

F. Programming Competition


A. Problemsolving Log

题意:A任务需要一分钟完成,B任务需要两分钟完成,……以此类推,给定一串任务s,由大写英文字母组成, 第i个字符表示完成了s【i】,问能完成多少个任务

思路:统计一下每个字符出现的次数,然后根据题意判断一下即可

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;cin>>t;
    while(t--)
    {
        int n;cin>>n;
        string s;
        cin>>s;
        // cout<<s<<endl;
        int cnt=0;
        map<char,int>mp;
        vector<bool>st(26,0);

        for(int i=0;i<n;i++)
        {
            mp[s[i]]++;
            int c=(int)(s[i]-'A')+1;
            // cout<<c<<endl;
            if(mp[s[i]]>=c&&!st[s[i]-'A'])cnt++,st[s[i]-'A']=1;
            // if(s[i]<='a'+i)cnt++;
        }
        cout<<cnt<<"\n";
    }
    return 0;
}

B. Preparing for the Contest

题意:给定一串序列,如果一个第i个数字大于第i-1个数字,那么就会兴奋一次,第一个数字不会兴奋,给定一个n和k,要求构造一个长度为n的序列,满足恰好兴奋k次

思路:从小到大顺序输出k个数字,再从大到小逆序输出n-k个数即可

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;cin>>t;
    while(t--)
    {
        int n,k;cin>>n>>k;
        for(int i=n-k;i>=1;i--)cout<<i<<" ";
        for(int i=n-k+1;i<=n;i++)cout<<i<<" ";
        cout<<endl;
    }
    return 0;
}

C. Quests

题意:给定n个任务,只有当第i个任务前的所有任务都至少完成过一次后,才能完成第i个任务,每个任务可以完成若干次,第一次完成会获得a_{i}的经验值,后面再次完成会获得b_i的经验值,给定一个数字k,问在k次内能取得的最大经验值

思路:从第一个任务开始,对于每个任务,我们可以选择继续做下一个任务,或者选择之前做过的任务中经验值最大的任务,不断遍历,去max即可

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;cin>>t;
    while(t--)
    {
        int n,k;cin>>n>>k;
        vector<int>a(n),b(n);
        for(int i=0;i<n;i++)cin>>a[i];
        for(int i=0;i<n;i++)cin>>b[i];

        int ans=0,sum=0,maxv=0;
        for(int i=0;i<min(n,k);i++)
        {
            sum+=a[i];
            maxv=max(maxv,b[i]);
            if(k-i-1>=0)ans=max(ans,sum+(k-i-1)*maxv);
            ans=max(ans,sum);
        }
        cout<<ans<<"\n";
    }
    return 0;
}

D. Three Activities

题意:给定3个序列a,b,c,要求找到a_{i}+b_{j}+c_{k}最大,且i,j,k互不相等

思路:对于每个序列进行从大到小的排序后,我们会发现,我们可以在每个序列中的前3个中各选一个找到一个满足题意的答案,暴力计算一下即可,时间复杂度为O(3^{3}).

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

#define x first
#define y second

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;cin>>t;
    while(t--)
    {
        int n;cin>>n;
        vector<pii>a(n),b(n),c(n);
        for(int i=0;i<n;i++)cin>>a[i].x,a[i].y=i;
        for(int i=0;i<n;i++)cin>>b[i].x,b[i].y=i;
        for(int i=0;i<n;i++)cin>>c[i].x,c[i].y=i;

        sort(a.begin(),a.end(),greater<pii>());
        sort(b.begin(),b.end(),greater<pii>());
        sort(c.begin(),c.end(),greater<pii>());

        int ans=0;
        for(int i=0;i<min(n,5);i++)
        {
            for(int j=0;j<min(n,5);j++)
            {
                for(int k=0;k<min(n,5);k++)
                {
                    if(a[i].y==b[j].y||a[i].y==c[k].y||b[j].y==c[k].y)continue;
                    ans=max(ans,a[i].x+b[j].x+c[k].x);
                }
            }
        }
        cout<<ans<<"\n";
    }
    return 0;
}

E1.E2. Game with Marbles

题意:有两个人Alice和Bob,他们有n种糖果,Alice拥有每种糖果a_{i}个,Bob拥有每种糖果b_{i}个,从Alice开始,每次可以选择一个两人都有的糖果i(即a_{i}>0,b_{i}>0),然后对于第i个糖果,a_{i}=a_{i}-1,b_{i}=0。下一回合轮到Bob,Bob同样选择一个两人都有的糖果i(即a_{i}>0,b_{i}>0),然后对于第i个糖果a_{i}=0,b_{i}=b_{i}-1,如果两人都不能选择一个糖果i进行上述操作,则结束操作,得分就是Alice的糖果总数减去Bob的糖果总数,Alice希望得分尽可能的大,Bob希望得分尽可能的小,问如果双方都进行最优操作,最后的得分是多少/

思路:

可以考虑下为什么 Alice 会选择颜色 i,而非选择颜色 j:

  • 如果 Alice 选了颜色 i , Bob 选了颜色 j ,这一回合的得分就是(a_{i}-1)+(1-{b_{j}})=a_{i}-b_{j}
  • 如果 Alice 选了颜色 j , Bob 选了颜色 i ,这一回合的得分就是 (a_{j}-1)+(1-b_{i})=a_{j}-b_{i}
  • 所以需要满足 a_{i}-b_{j}>a_{j}-b{i}->a_{i}+b_{i}>a_{j}+b_{j}

因此我们对 a_{i}+b_{i} 进行排序就可以了。

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

#define x first
#define y second

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;cin>>t;
    while(t--)
    {
        int n;cin>>n;
        vector<pii>a(n),b(n),c(n);
        for(int i=0;i<n;i++)cin>>a[i].x,a[i].y=i;
        for(int i=0;i<n;i++)cin>>b[i].x,b[i].y=i;
        for(int i=0;i<n;i++)cin>>c[i].x,c[i].y=i;

        sort(a.begin(),a.end(),greater<pii>());
        sort(b.begin(),b.end(),greater<pii>());
        sort(c.begin(),c.end(),greater<pii>());

        int ans=0;
        for(int i=0;i<min(n,5);i++)
        {
            for(int j=0;j<min(n,5);j++)
            {
                for(int k=0;k<min(n,5);k++)
                {
                    if(a[i].y==b[j].y||a[i].y==c[k].y||b[j].y==c[k].y)continue;
                    ans=max(ans,a[i].x+b[j].x+c[k].x);
                }
            }
        }
        cout<<ans<<"\n";
    }
    return 0;
}

F. Programming Competition

这题地题解是看这位大佬的:https://zhuanlan.zhihu.com/p/673159169

题意:给定一棵树,每个节点除了不可以跟其祖宗节点进行匹配,其余都可以匹配。问最大匹配数。

思路:有一个很经典的题目就是,如果有n种不同类型的糖果,总数为s,两个不同的糖果可以配对成一组,问最多配对多少组。

显然,如果数量最多的糖果不超过总数的一半,那我们总是可以配对出\lfloor \frac{s}{2} \rfloor组。否则我们就无法用完数量最多的糖果,假设最大的糖果有mx个,那我们只能配对出s-mx组。

回到这题,对于每个节点,我们把不同子树内的点认为是不同的类型。所以如果出现当前最大的子树里点不超过当前节点的所有子节点总数的一半,那我们可以把剩下所有点都配对,然后退出即可。否则我们肯定是把其他子树内的点和最大的子树内的点配对完之后再递归处理最大的子树。

递归到子树的时候,某些该子树内的点已经在之前配对过了,显然应该贪心地让当前子树地根与外面地点配对,这样一定是最优地,因为根不可能和子树地任何点配对

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 2e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    int t;cin>>t;
    while(t--)
    {
        int n;cin>>n;
        vector<vector<int>>g(n+1);//注意这里要用这种写法,不能写成vector<int>g[n+1],否则dfs会报错,不知道为什么
        for(int i=2;i<=n;i++)
        {
            int x;cin>>x;
            g[x].push_back(i);
        }

        vector<int>sz(n+1);//记录每个节点的子节点个数

        //统计每个节点的子节点个数
        auto dfs1 = [&](auto &&dfs1,int u)->void    
        {
            sz[u]=1;
            for(auto &j:g[u])
            {
                dfs1(dfs1,j);
                sz[u]+=sz[j];
                if(sz[j]>sz[g[u][0]])swap(j,g[u][0]);
            }
        };
        dfs1(dfs1,1);

        int ans=0;

        //找到最大匹配数,u为当前节点,c为上一轮能匹配的数量
        auto dfs2 = [&](auto &&dfs2,int u,int c)->void
        {
            if(g[u].empty())return;
            if(c>=sz[u])return;
            if(c>0)c-=1;//减1表示减去的是根节点,将根节点跟其他节点匹配

            int sum=0;//统计该节点的子节点个数
            for(auto j:g[u])sum+=sz[j];
            int mx=sz[g[u][0]];//该节点的子节点中节点个数最多的点

            if(mx-c<=sum-mx)//说明当前节点的最大子节点个数减去上一轮与其他节点匹配的数量之后小于总数的一半
            {
                ans+=(sum-c)/2;//那么答案就是总数的一半
                return;
            }

            ans+=sum-mx;//否则记录下来此时能匹配的最多的个数
            c+=sum-mx;//记录下来能匹配的最多的个数
            dfs2(dfs2,g[u][0],c);
        };
        dfs2(dfs2,1,0);
        cout<<ans<<"\n";
    }    
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值