部分题解
- 7-4 键盘故障
- 7-6 筷子
- 7-8 方
- 7-9 优美的字符串
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 筷子
贪心,需要冷静分析模拟
- 题目一个重要的点是,无论怎么取k只筷子都能凑成m双,对于取每一种筷子,无非就两种情况,取奇数个,或者取偶数个,最严苛的取筷子的方法就是每种都取奇数个,每种取奇数个都可以凑成m双,每种取比它小1只的偶数个也能凑成m双,就怕你取的全都是奇数个,每种都要多取一个出来
- 但是有个问题,最终每种都是奇数个,能凑成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 优美的字符串
- 简单的dp问题,但是dp问题难就难在需要自己能够识别出来是个dp问题,就需要多练题,这个题与之前的一个刷房子问题比较像
- 回文字符串:正向读字符串和反向读字符串是一样的
- 题目说不存在长度严格大于 2 的回文串,也就是说只能是a,aa这种,我们可以从根源上砍断长度大于2的回文串,也就是从长度为3的串下手,如果字符串中没有aaa、aba这种形式的回文串,就一定不存在长度严格大于2的回文串,因此我们每次只考虑最后三个字符串
- 定义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形式,无法转移 - 边界需要单独判断,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;
}