前言:本次测试是在家通过网络提交进行的,不出所料,我又考得十分“爆炸”,炸到我怀疑人生。
另外,由于以前有一些题目迟迟没有改完,所以我打算改完哪场比赛的题目就先写那场比赛的总结,避免遗忘和拖沓,虽然可能会有些乱。。。
a 最大得分
题目描述
“回文分数”游戏并不简单。游戏的目标是修改最多maxChanges个字符使得一个字符串word的回文分数最高。只允许修改,不许增加或者删除字符。
一个字符串的回文分数定义如下:
1、如果字符串不是回文串,则分数为0。
2、如果字符串是回文串,且长度为奇数,则分数为1。
3、如果字符串是回文串,且长度为偶数,我们将它分为左右两半。计算它的一半子串的回文分数为K(两个一半子串得分一定相同),则原字符串的回文分数为K + 1。
给定一个字符串word和一个型整数maxChanges,返回最多修改maxChanges个字符后最大可能的回文分数。
回文串的定义是一个字符串从前向后读和从后向前读完全一样。
输入格式
第一行:一个字符串 word
第二行:一个整数 maxChanges
word包含1到50个字符(含1和50)。
word 只包含小写字母 (‘a’-‘z’)。
maxChanges 取值范围是0到50(含0和50)。
输出格式
第一行: 一个整数 maximize
输入样例
输入样例一:
abcbxabcba
1
输入样例二:
coder
2
输出样例
输出样例一:
2
样例解释:
如果把x改成a,得到偶数长度的回文串”abcbaabcba”。它的一半子串是奇数长度的回文串”abcba”,所以子串分数为K = 1,因而最后得分是K + 1 = 2。
输出样例二:
1
样例解释:
我们可以把c改成r,把e改成o,得到”rodor”。这是一个奇数长度的回文串,所以得分为1。
解题思路(模拟+贪心)
这题做得我十分痛苦,其实这是一道比较简单的贪心+模拟题。我一开始的做法就是照着题目要求模拟,在一次比较,两个字符不同时,关键就是如何替换才使答案最优。如果随便替换的话将对后面的答案有影响。所以我当时的做法是处理一层的替换的时候看看下一层,怎样替换对下一层更优我就怎样替换。本质就是看看字符的出现个数。
然而这样未必对,我们知道,如果当前层对下一层一样优的话,对下下层可能不一样。但是我这样做也有94分,好一段时间内我不知道该怎么改。其实必须边递归边展开所有层判断是否能继续,再统计答案。由于层数不超过6层,所以这样不会超时。但这样的方法其实并不直观们还有一种更易理解的方法。
直接枚举答案,假如长度为12,枚举的答案为2,则可以表示为1 2 3 3 2 1 1 2 3 3 2 1,然后将相同数字的位置改成相同字符即可。如何改就贪心,改成哪个字符修改次数少就那么改,再和maxChanges比较判断答案。
Code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#define N 60
using namespace std;
char word[N];
int len, change, score, num[N];
int check(int a, int b, int l){
memset(num, 0, sizeof(num));
for(int i = 0; b+i*l < len; i++){
num[word[a+i*l]-'a'] ++;
if(a != b) num[word[b+i*l]-'a'] ++;
}
int sum = 0, Max = 0;
for(int i = 0; i < 26; i++) sum += num[i], Max = max(Max, num[i]);
return sum - Max;
}
int dfs(int l){
if(l & 1){
int res = 0;
for(int i = 0; i <= l/2; i++) res += check(i, l-i-1, l);
if(res <= change) return 1;
return 0;
}
else{
int res = 0;
for(int i = 0; i < l/2; i++) res += check(i, l-i-1, l);
if(res <= change) return dfs(l/2) + 1;
return 0;
}
}
int main(){
scanf("%s", &word);
scanf("%d", &change);
len = strlen(word);
score = dfs(len);
printf("%d\n", score);
return 0;
}
b 寄存器
题目描述
你有一台超小的电脑,内存只有两个寄存器:X和Y。寄存器只能存储正整数,一开始两个寄存器的值都是1,电脑操作系统只有两种指令:指令[X]和指令[Y]。
指令[X]的功能是:X ← X + Y,即把两寄存器目前的值累加到X寄存器;
指令[Y]的功能是:Y ← X + Y,即把两寄存器目前的值累加到Y寄存器。例如:指令序列”XXYYX”的执行过程如下:
可以发现,执行指令序列”XXYYX”后,X寄存器的值是10,Y寄存器的值是7。现在你的任务是:给你一个正整数R, 你要编写指令序列,使得最后X寄存器的值是R (此时Y寄存器可以是任意整数). 当然,我们希望你编写的指令序列的长度要尽量短,在此前提下,如果有多种方案,请输出字典序最小的一种方案。
输入格式
多组测试数据。
第一行,一整正整数G,表示有G组测试数据,1 <= G <=5
每组测试数据格式:
一个正整数R,其中 1 <= R <= 1000000。
输出格式
一个字符串,代表生成R的长度最短的指令序列。
输入样例
4
10
3
20
34
输出样例
XXYYX
XX
XYYYYXX
XYXYXYX
解题思路(更相减损术(迭代)+gcd)
如果直接宽搜,必定超时。
我们必须注意到一点,如果我们枚举一个Y,是可以直接根据最短步数得到方案的。
这个得到方案的方法就是在数学上有了解到的更相减损术。
容易证明,我们不断用大数减小数,得到的差和较小数再次重复做这个操作,不断迭代,步数一定是最短的。根据一些方法,我们可以知道可能的最少步数不会超过三十几步,这里我是用斐波那契数列去发现的(然后考试时我就想歪了。。)。
于是时间就是枚举Y(1e6)然后乘上个三十几。至于枚举的Y不一定是合法的,合法的前提是与给出的X互质,为什么呢?因为更相减损术就是在求gcd啊!于是不与X互质的Y必然不会产生合法答案。然后就写个欧几里德算法判断,时间照样不成问题。不过考试时我思维僵化,连方向都错了。
字典序就做完一次判断更新答案。如果算的过程中,答案长度已经超过可能的最优长度(三十几)或当前答案,就不用往下做了,直接跳过,这样可以节省很多时间。最后倒叙输出答案就行了。
坑点:第一是特判,然后不要用字符串,要用字符数组记录答案,否则超时。。
Code
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#define oo 0x7fffffff
#define ban 32
using namespace std;
int G, R;
char ans[40], res[40];
int len;
int Gcd(int a, int b){
if(!b) return a;
return Gcd(b, a % b);
}
void Work(int X, int Y){
int l = 0;
while(X != Y){
if(X > Y){
res[l++] = 'X';
X = X - Y;
}