HBCPC2022-河北省大学生程序设计竞赛

7-4 键盘故障

签到题,直接遍历字符串,相同则跳过,不相同则输出
题目链接:https://pintia.cn/problem-sets/1584003400735793152/exam/problems/1584003481883000835
AC代码:

#include<iostream>
using namespace std;
string sa;
int main()
{
    cin>>sa;
    char pre=' ';
    for(int i=0;i<sa.length();i++)
    {
        if(sa[i]!=pre)
        {
            cout<<sa[i];
            pre=sa[i];
        }
    }
    return 0;
}

7-6 筷子

贪心,需要冷静分析模拟

  1. 题目一个重要的点是,无论怎么取k只筷子都能凑成m双,对于取每一种筷子,无非就两种情况,取奇数个,或者取偶数个,最严苛的取筷子的方法就是每种都取奇数个,每种取奇数个都可以凑成m双,每种取比它小1只的偶数个也能凑成m双,就怕你取的全都是奇数个,每种都要多取一个出来
  2. 但是有个问题,最终每种都是奇数个,能凑成m双筷子,要保证取的筷子数最少,为啥不每种取比它小的偶数个呢,这样每种还能少取一只筷子,但是假如你少取2个,你能保证你是对不同种类的筷子分别少取一个吗,不能啊,你如果只在一种筷子里面取,那你的最终筷子的对数就比m少了,因此只能对其中一种筷子少取一个
    在这里插入图片描述
    感觉贪心就是一种启发式算法,想到位了,感觉对了,就成了
    注意:本题的输入输出数据很多,cin和cout很耗时,加上tie(0)仍然很费时间,所以需要换成scanf和printf
    AC代码:
#include<iostream>
#include<stdio.h>
using namespace std;
typedef long long LL;
LL t,n,m;
int main()
{
    scanf("%ld",&t);
    for(LL i=0;i<t;i++)
    {
        scanf("%ld %ld",&n,&m);
        LL dui=0,zhi=0,sheng=0;
        for(LL j=0;j<n;j++)
        {
            LL t;scanf("%ld",&t);
            if(t%2) //如果是奇数个就全取
            {
                zhi+=t;dui+=t/2;
            }
            else   //偶数个取小于等于该偶数的最大奇数个
            {
                zhi+=(t-1); dui+=(t-1)/2;
                sheng+=1;    //剩下的那一个备用
            }
        }
        
        if(dui>=m) //取多了,减少几对
        {
            zhi-=(dui-m)*2;
            zhi-=1;
        }
        else if(dui==m)   zhi-=1;  //全是奇数个可以,但是其中一种鞋子减少一个也可以
        else  //取得还不够,从剩下的里面去凑
        {
            if(sheng>=m-dui)  zhi+=(m-dui);  //剩下的足够
            else zhi=-1;   //剩下的凑不齐m对了
        }
        printf("%ld\n",zhi);
    }
    return 0;
}

官方题解思路:(贪心靠感觉,思路各有各的道理)
在这里插入图片描述

7-8 方

签到题,直接输出答案
题目链接:https://pintia.cn/problem-sets/1584003400735793152/exam/problems/1584003481883000839
AC代码:

#include<bits/stdc++.h>
using namespace std;
int t,n;
int main()
{
    cin>>t;
    for(int i=0;i<t;i++)
    {
        cin>>n;
        double tmp=pow(2,0.5);
        cout<<fixed<<setprecision(2)<<pow(tmp,n-1)<<endl;
    }
    return 0;
}

7-9 优美的字符串

  1. 简单的dp问题,但是dp问题难就难在需要自己能够识别出来是个dp问题,就需要多练题,这个题与之前的一个刷房子问题比较像
  2. 回文字符串:正向读字符串和反向读字符串是一样的
  3. 题目说不存在长度严格大于 2 的回文串,也就是说只能是a,aa这种,我们可以从根源上砍断长度大于2的回文串,也就是从长度为3的串下手,如果字符串中没有aaa、aba这种形式的回文串,就一定不存在长度严格大于2的回文串,因此我们每次只考虑最后三个字符串
  4. 定义dp数组和状态转移方程
    dp[i][0] 长度为i的字符串结尾为abc形式的方案数
    dp[i][1] 长度为i的字符串结尾为aab形式的方案数
    dp[i][2] 长度为i的字符串结尾为abb形式的方案数
    状态转移:
    dp[i][0] = dp[i-1][0] * (m-2) +dp[i-1][1] * (m-2)
    abc
    dp[i-1][0]: abc 往最后插入一个字符,形成的新字符串最后三个不相等,只需要插入字符不等于b和c ,可选m-2种字符
    dp[i-1][1]: aab 往最后插入一个字符,形成的新字符串最后三个不相等,只需要插入字符不等于a和b ,可选m-2种字符
    dp[i-1][2]: abb b和b相等,往最后插入一个字符,形成的新字符串最后三个不可能不相等,无法转移
    dp[i][1] = dp[i-1][2]*(m-2)
    aab
    dp[i-1][0]:abc b和c不相等,往最后插入一个字符,形成的新字符串前两个字符不可能相等,无法转移
    dp[i-1][1]:aab 同上
    dp[i-1][2]:abb 往最后插入一个字符,形成的新字符串最后三个为bbc,只需要插入字符不等于a和b ,可选m-2种字符
    dp[i][2] = dp[i-1][0]+ dp[i-1][1]
    abb
    dp[i-1][0]:abc 往最后插入一个字符,形成的新字符串最后三个为bcc,插入字符必须为c
    dp[i-1][1]:aab 往最后插入一个字符,形成的新字符串最后三个为abb,插入字符必须为b
    dp[i-1][2]:abb b和b相等,往最后插入一个字符,形成的新字符串最后三个不可能为abb形式,无法转移
  5. 边界需要单独判断,dp数组从dp[3]开始计算,并且dp[3]是跟m有关的,需要进行一下推演,取余的问题就看当前相加或者相乘得到的数会不会超过p,会超过就需要取余
    AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,m;
const LL p= 1000000007;
const LL N=1000005;
LL dp[N][3];
int main()
{
    cin>>n>>m;
    if(n<=2)   //所有组合字符串都满足
    {
    	LL ans=pow(m,n);
        cout<<ans%p;
        return 0;
    }
    dp[3][0]=((m*(m-1))%p*(m-2))%p;   
    dp[3][1]=(m*(m-1))%p;
    dp[3][2]=(m*(m-1))%p;
    for(LL i=4;i<=n;i++)
    {
        dp[i][0] = ((dp[i-1][0]*(m-2))%p +(dp[i-1][1]*(m-2))%p)%p;
        dp[i][1]  = (dp[i-1][2]*(m-2))%p;
        dp[i][2]  = (dp[i-1][0]%p+ dp[i-1][1]%p)%p;
    }
    cout<<(dp[n][0]+dp[n][1]+dp[n][2])%p;
    return 0;
}
  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值