A. Lucky Substrings
这道题并不难,由于字符串长度只有100,那么它的子串肯定不超过1w个,枚举出所有字串都是没有问题的,至于检验一个子串里面不同的字母数量是不是斐波那契数,我们只需要事先把斐波那契数列小于1w的项都生成出来,然后枚举一个子串之后,统计出不同字母的数量(边找边统计,如果当前字母之前出现过就不加,如果没出现过就记住并+1),去这个里面找就行了。斐波那契数列推不了几项就到1w了……
最后要字典序从小到大排列,并且还要去重,那就用string然后直接sort+unique一发,搞定
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<string>
#include<algorithm>
using namespace std;
char s[200];
string ans[20000];
int p_ans=0;
int fib[10000];
int p_fib=0;
void gener_fb()
{
fib[1]=1;
fib[2]=1;
int i=3;
while(true)
{
fib[i] = fib[i-1]+fib[i-2];
if(fib[i]>100)
{
p_fib =i;
return;
}
i++;
}
}
bool find(int x)
{
for(int i=1; i<=p_fib;i++)
{
if(fib[i] == x)
return true;
}
return false;
}
int main()
{
gener_fb();
cin>>s;
int len=strlen(s);
int i;
for(i=0;i<len;i++)
{
char now[200]={0};
int appear[200]={0};
int sum_appear=0;
for(int j=i; j<len; j++)
{
now[j-i] = s[j];
if(appear[s[j]]==0)
{
appear[s[j]]=1;
sum_appear ++;
}
if(find(sum_appear))
{
string str(now);
ans[++p_ans] = str;
}
}
}
sort(&ans[1], &ans[p_ans+1]);
p_ans = unique(&ans[1], &ans[p_ans+1]) - ans-1;
for(i=1;i<=p_ans;i++)
{
cout<<ans[i]<<endl;
}
//system("pause");
return 0;
}
B. Numeric Keypad
这道题我还是想了一会的,我也不知道其他人是怎么做的……
这道题可以注意到这么几点,首先就是输入的数特别大,有500位,所以我们肯定是要一位一位处理,每一位都要尽量和输入的一样,因为如果前面有一位小了,后面再大也没用;
关键问题是有的时候如果你输的一样的话,后面就不行了,比如样例的131,你如果第二位输了3,那你会发现第三位无解了。怎么办呢?其实很容易,这个时候我们只需要回退到上一层并尝试一个更小的数字即可。你会发现这样的回退最多只会出现一次,因为一旦回退之后,后面的你都可以无脑的填了。如果回退的那一位不是0的话,那么你后面可以无脑填9,比如131你回退到12的时候,后面你都不用想了,肯定全填9,反正你在第二位已经比他小了;如果回退这一位是0的话就不行了,因为0到不了9,就只能后面全无脑填0了,比如876这个例子答案就是800.
建议预先处理一个map[i][j]
,表示数字i能不能到数字j去,这样会给你后面省很多事。
#include<iostream>
#include<string.h>
#include<math.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
char input[600];
int t;
int map[10][10];
int ans[600];
void initmap()
{
int i,j;
for(i=1;i<=9;i++)
{
for(j=1;j<=9;j++)
{
if( (i-1)/3 <= (j-1)/3 && (i-1)%3 <=(j-1)%3)
map[i][j]=1;
}
}
for(i=1;i<=7;i+=3)
map[i][0]=1;
for(i=2;i<=8;i+=3)
map[i][0]=1;
map[0][0]=1;
return;
}
bool go(int pos, int last)
{
if(pos==strlen(input))
return true;
int now = input[pos]-‘0‘;
bool ok=false;
for(int i=now; i>=0; i--)
{
if(map[last][i]==1)
{
ans[pos]=i;
if(i==now)
{
if(go(pos+1, i) == false)
{
continue;
}
else
return true;
}
else
{
for(int tmp=pos+1; tmp<strlen(input); tmp++)
{
if(i==0)
ans[tmp]=0;
else
ans[tmp]=9;
}
return true;
}
}
}
return false;
}
void init()
{
memset(ans,0,sizeof(ans));
}
int main()
{
initmap();
cin>>t;
for(int files=1; files<=t; files++)
{
init();
cin>>input;
go(0,1);
for(int i=0; i<strlen(input);i++)
cout<<ans[i];
cout<<endl;
}
//system("pause");
return 0;
}
C. Spring Outing
这题当时我的算法WA了,然后想了半天也没想出来哪儿错,后来今天讨论才知道的……
这道题目的关键就在于“已经知道的肯定可以去的地方”,一开始大家都知道“我至少可以待在家里”。
那么考虑这么一个事情,对于某一个人来说,如果前面所有的地点都被pass掉了,现在投K的话,如果K在0后面,那我肯定不投;如果K在0前面,那我肯定就要投,不然没别的选择我就得在家呆着了;这样,我们根据所有人的行为就能知道K到底能不能去,如果有一半人都会投K的话,那么K就可以去了,反之K就不能去;
同样的道理,如果K可以去了,我们看K-1的时候,如果某个人更想去K-1,那他一定会投的,否则一定不会投的。当然如果刚才结果是K去不了,那么现在最后一个肯定能去的选择就是0,就变成了K-1和0之间的选择。
所以,我们只需要从第K号地点往前试,试完K个就知道他们会在哪个地点达到第一次(也就是我们这个算法当中的最后一次)过半数了。
由于输入都是0~n的排列,所以我们可以将每个数在每个人当中的位置记录下来,这样找起来就非常方便了。
注意,并不是说一个人绝对不会投一个已知可取的地方的后面的票的,比如一个人的志愿是0 1 2,那么根据刚才的结论,如果2已经可以去了,他还是会投1的,要不然就铁定去2了。并不是说一个人想在家呆着就一定会一直憋着。
本题由于还没有开放,所以就不贴代码了。