P3612 [USACO17JAN]Secret Cow Code S
通过观察可以知道,其中后半部分的字符是前半部分字符的复制,比如cow->cowwco,前两个字符co在后面也可以找到,而且他们的相对位置是有规律的,公式就是s[i]=s[i-len/2-1].加设字符串下标从1开始,那么最后一个字符o对应的i就是6,将i带入公式,那么i-6/2-1=6-3-1=2,会发现s[2]=o.这个规律并不是对所有后半部分的字符都通用,比如w,当i=4的时候,i-6/2-1=4-4-1=0,而我们规定的字符是下标为1开始,下标为0的字符没有意义。所以对于后半部分字符的第一个字符,需要特殊处理。
我们知道,这种‘无限字符串’每次变化都是翻一倍,所有我们只有严格按照上面说的规律,每次找到它前一半的相应值,在往前找,就可以找到最开始的字符串里面对应的字符。
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
char s[1000];
int main()
{
cin>>s;
ll n;
cin>>n;
int s_len=strlen(s); //标记初始字符串的长度
while(n>s_len) //目的是将n缩减到初始字符串长度以内
{
ll k=s_len;
while(k<n) //k计算的是当前n所在的字符串长度是多少
k*=2;
n=n-k/2-1; //n所在的字符串按照上面说的规律去操作
if(n==0) //对于0要特判,上面也说了
n=k/2;
}
cout<<s[n-1]; //记得-1
return 0;
}
P1259 黑白棋子的移动
这道题规律很明显,把当前最右边的白棋子和邻接它的黑棋子与后面两个横线互换。然后横线与邻接的两个黑棋子互换,一直这样到白棋子剩下4颗。对于剩下的4颗白棋子,找不到规律,那就硬抄好了。
#include<iostream>
#include<cstring>
using namespace std;
char s[10000];
//ooo--*** o* o* o* o*
//ooo* o** --* o* o* o*
//o--* o** oo* o* o* o*
//o* o* o* --o* o* o* o*
char last[4][11] = { "ooo--***o*","ooo*o**--*","o--*o**oo*","o*o*o*--o*" }; //记录最后4步
void change(char *a, char *b) //交换棋子函数
{
char temp;
temp = *a;
*a = *b;
*b = temp;
}
int main()
{
/*ooooooo******* --
oooooo--****** o*
oooooo****** --o*
ooooo--***** o* o*
ooooo***** --o* o*
oooo--**** o* o* o*
oooo**** --o* o* o*
ooo--*** o* o* o* o*
ooo* o** --* o* o* o*
o--* o** oo* o* o* o*
o* o* o* --o* o* o* o*
--o* o* o* o* o* o* o**/
int n;
cin >> n; //n表示黑白各n个
for (int i = 1; i <= n; i++) //白
s[i] = 'o';
for (int i = n + 1; i <= n * 2; i++) //黑
s[i] = '*';
s[2*n + 1] = s[2*n + 2] = '-'; //后面的横线
for (int i = 1; i <= 2 * n + 2;i++)
{
cout << s[i];
}
cout << endl;
int length = n;
int exchange_count = n - 4; //最后剩下4个白棋子不动
int end_cout = 0;
for (int i = 1; i <= exchange_count; i++)
{
change(s+length, s+(2 * length + 1)); //s下标从1开始
change(s+(length + 1), s+(2 * length + 2));
for (int i = 1; i <= 2 * n + 2;i++)
{
cout << s[i];
}
cout << endl;
change(s+(length), s+(2 * length - 1));//
change(s+(length + 1),s+(2 * length));
for (int i = 1; i <= 2 * n + 2; i++)
{
cout << s[i];
}
cout << endl;
length--;
end_cout += 1; //后面'o*'d的个数
}
for (int i = 0; i < 4; i++)
{
cout << last[i];
for (int j = 0; j < end_cout; j++)
cout << "o*";
cout << endl;
}
cout << "--";
for (int i = 0; i < end_cout + 4; i++)
{
cout << "o*";
}
return 0;
}
P1010 [NOIP1998 普及组] 幂次方
这道题就是一个简单的递归了,条件如下:
如果说当前2的次幂为0或者为2,输出2(0),2(2),否则输出(,然后继续递归。输出完)括号的时候,输入+。就这样
#include<iostream>
#include<cstdio>
using namespace std;
void dfs(int k) //k是当前要分解的数
{
if (k == 0)
return;
int m = 1; //m是要找到最接近k的数,且m>=k
int m_2=0; //记录m的次方,也是2的多少次方
while (m <= k)
{
m *= 2;
m_2 += 1;
}
//跳出循环的时候m>=k,所以k是2的m_2-1次幂,比如k=3,则m=4,m_2=3,m_2-1=2。
printf("2");
if (m_2 - 1 == 0 || m_2 - 1 == 2) //次方为0或2直接输出
{
printf("(%d)", m_2 - 1);
}
else if (m_2 - 1 > 2) //否则继续递归
{
printf("(");
dfs(m_2 - 1);
printf(")");
}
k = k - m / 2; //递归完出来减去处理完的部分然后加上+号继续递归
if (k > 0)
{
printf("+");
dfs(k);
}
}
int main()
{
int n;
cin >> n;
dfs(n);
}