1406 String Game
一、链接
1406 String Game
二、题目
题目描述
Alice和Bob正在玩一个基于字符串的游戏,一开始,Alice和Bob分别拥有一个等长的字符串S1和S2,且这两个字符串只包含小写字母。 在每个回合中,Alice和Bob必须分别选择自己的字符串的某一个位置并把这个位置上的字母改变为其他小写字母。 经过P个回合后,他们的得分分别等于自己的字符串中出现最多的字母出现的次数。 最终得分高者获胜,如果两人得分相等,则为平局。 现在你知道了初始的两个字符串S1、S2和回合数P,如果两人都以最优策略游戏,请问最后谁能获胜或者结果是平局。
输入
第一行是一个数T(1≤T≤100000),表示样例的个数。 然后每个样例第一行两个数字,分别是字符串长度N和回合数P,(1≤N≤100,0≤P≤109)。 接下来两行是两个字符串S1,S2,分别是Alice和Bob的初始字符串。
输出
对于每个样例,输出一行。如果Alice能获胜,输出"Alice",如果结果是平局,输出"Draw",否则输出"Bob"。
样例输入
4 6 2 xxxttu xxttuu 6 5 xxxttu xxttuu 6 3 xtuxtu xxttuu 4 0 alic ebob
样例输出
Alice Draw Draw Bob
提示
巨大的输入,请使用C风格的输入
作者
代卓岑
三、题意
输入两个长度相等的字符串,经过多次改变,把某一个字母变成另一个字母,把出现次数最多的字母出现的次数作为分数,输出分数高的人的名字,或者是平局
四、代码
c++代码
#include <iostream>
#include<cstring>
using namespace std;
char s1[101],s2[101];
int a[30]={0},b[30]={0},countalicemax=0,countbobmax=0,scorealice=0,scorebob=0,n,p;
int main()
{
int k;
scanf("%d", &k);
while (k--)
{
scanf("%d%d", &n, &p);
scanf("%s", s1);
scanf("%s", s2);
for(int i=0;i<n;i++)
{
a[s1[i] - 'a']++;
if (a[s1[i] - 'a'] > countalicemax) countalicemax = a[s1[i] - 'a'];
b[s2[i] - 'a']++;
if (b[s2[i] - 'a'] > countbobmax) countbobmax = b[s2[i] - 'a'];
}
if (countalicemax == n)
{
if (p == 1) scorealice = n - 1;
else scorealice = n;
}
else if (p >= n - countalicemax) scorealice = n;
else scorealice = p + countalicemax;
if (countbobmax == n)
{
if (p == 1) scorebob = n - 1;
else scorebob = n;
}
else if (p >= n - countbobmax) scorebob = n;
else scorebob = p + countbobmax;
if (scorealice > scorebob) printf("Alice\n");
else if (scorealice < scorebob) printf("Bob\n");
else printf("Draw\n");
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
countalicemax=0,countbobmax=0,scorealice=0,scorebob=0;
}
return 0;
}
五、总结
1.把数组变量重置的时候,需要用循环来进行重置,或者使用memset函数来进行重置
memset(a,0,sizeof(a));
这个函数在cstring头文件里面,第一个参数是数组名,第二个参数是修改后的值,第三个参数是修改个数
2. 首先找到每一个字符串出现次数最多的字母,记录这个字母出现的次数,并不断地维护这个出现次数最多的字母(会发生改变),直到遍历完所有元素,找到出现次数最多的字母和它出现的次数
for(int i=0;i<n;i++)
{
a[s1[i] - 'a']++;
if (a[s1[i] - 'a'] > countalicemax) countalicemax = a[s1[i] - 'a'];
b[s2[i] - 'a']++;
if (b[s2[i] - 'a'] > countbobmax) countbobmax = b[s2[i] - 'a'];
}
和这一道题有点相似:湘大 XTU OJ 1260 Completed String 题解(非常详细):建立数组下标和数组元素之间的映射关系 ~scanf
3.分类讨论:
(1)如果出现次数最多的字母出现的次数等于字符串的长度,如果回合数等于1,那么分数就等于字符串总长度减去1,其余的所有情况,分数都等于字符串总长度,比如说,xxxxxx,6个x,操作两个回合,按照最优策略,先选择一个x更换为除x之外的任意一个字母,假设是a,然后再把这个a换成x,所以字符串经过两个回合没有发生变化,操作三个回合,先把x换成a,再把a换成b,再把b换成x,字符串还是可以保持原来的6个x的状态
这里很容易想错,误以为和操作的回合数的奇偶性有关,其实是没有关系的,按照最优策略,只要操作的回合数大于1,就可以让字符串保持原来的状态
(2)总长度减去出现次数最多的字母的出现次数如果小于回合数,说明把不是出现次数最多的字母更改一次变成出现次数最多的字母之后,还需要进行几个回合的操作,比如说xxxttu,假设回合数是5,我们经过3次操作,可以把字符串变成6个x,这个时候还剩下2次操作机会,把x换成a,a再换成x,假设操作回合数是4,我们可以先把字符串变成xxxxxu,这个时候用掉了2个回合的操作机会,还可以操作两次,把u变成a,再把a变成x,也就是整个字符串变成6个x,所以这种情况下,分数等于字符串的总长度
(3)剩下的情况(字符串总长度-出现次数最多的字母出现的次数>=操作的回合数):把每一个不是出现次数最多的字母修改为出现次数最多的字母即可,比如说xxxttu,回合数等于2,把原来的字符串修改为xxxxxu即可,这种情况下,分数等于出现次数最多的字母的出现次数+回合数
4.按照1的描述重置所有变量,方便下一次比较
5.解题的关键是用具体的例子模拟整个过程,理顺整个过程,以及按照最优策略需要怎么更换字母,从特殊到一般的一个思考过程
1098 素数个数
链接:1098 素数个数
题目:
Description | ||
给定两个非负整数a,b,其中0<= a,b<=1,000,000,请计算这两个数之间有多少个素数。 输入 第一行是一个整数K(1<=K<=1000),表示有多少个样例,每个样例占一行,是两个整数a和b,每个整数之间用一个空格隔开。 输出 每行输出一个样例的结果。 | ||
Sample Input | ||
2 2 3 17 19 | ||
Sample Output | ||
2 2 | ||
Source | ||
ericxie |
代码:
#include<iostream>
using namespace std;
const int N=1e6+10;
int q[N];
bool isprime(int a)
{
if(a<2) return false;
for(int i=2;i*i<=a;i++)
if(a%i==0) return false;
return true;
}
void start()
{
for(int i=2;i<=N;i++)
if(isprime(i)) q[i]=1;
}
int main()
{
int t;
scanf("%d",&t);
start();
while(t--)
{
int a,b;
scanf("%d%d",&a,&b);
if(a>b) swap(a,b);
int cnt=0;
for(int i=a;i<=b;i++)
if(q[i]==1) cnt++;
printf("%d\n",cnt);
}
return 0;
}
总结:
1.这个题目可以说是打表做出来的,打表的意思是先把所有答案存储在一张表里面(数组),然后再从表格里面查询答案
2.判断是否是素数是一个算法模板
bool isprime(int a)
{
if(a<2) return false;
for(int i=2;i*i<=a;i++)
if(a%i==0) return false;
return true;
}
3.打表操作是把所有是素数的元素标记为1
void start()
{
for(int i=2;i<=N;i++)
if(isprime(i)) q[i]=1;
}
这里注意,把这个数组定义为了全局变量,也就是说,下标为0,1的元素初始化为0,表示不是素数,所以循环从2开始
4.在查询操作之前打表,也就是说只用进行一次打表过程,如果放到多样例的循环里面就会超时。
5.两个数字没有保证按照大小顺序输入,所以需要自己保证左边的数字小于右边的数字
if(a>b) swap(a,b);
6.定义局部变量并且初始化,这样就不需要每一次循环之后重置,非常方便(算是经验了哈哈)