D - aab aba baa
题意: 就是给你A个‘a’和B个‘b’,由a和b组成的所有字符串按字典序排列,输出第k个字符串。
思路:和全排列有关,对于所有的排列情况,就是C(a+b,a).假设n=A+B.
假设第一个字母你确定为‘a’那么你看剩下A-1个‘a’和B个‘b’的组合情况。
如果小于K,说明你选‘a’是不合法的。不合法的原因是,C(n-1,a-1)这个数比k还小,说明现在以a开头的字符串在字典序的顺序中已经在第k个之前了,只能选‘b’了。来使字符串在字典序中的顺序靠后。
如果大于等于K, 说明选‘a’是合法的.合法的原因是,C(n-1,a-1)这个数比k还大,说明现在以a开头的字符串在字典序的顺序中已经在第k个之后了(或者等于),可以靠后面的字符串的组合来使字符串在字典序中的位置处在第K位置。
所以,需要预处理出来组合数来,组合数的处理需要用
C
(
i
,
j
)
=
C
(
i
−
1
,
j
)
+
C
(
i
−
1
,
j
−
1
)
C(i,j)=C(i-1,j)+C(i-1,j-1)
C(i,j)=C(i−1,j)+C(i−1,j−1)
当j等于0时,
C
(
i
,
0
)
=
1
C(i,0)=1
C(i,0)=1。
来求出所有的组合数情况,然后在利用上面的假设,从前往后对每个位置都进行判断选‘a’还是选‘b’,选‘b’的时候,K要减去当前位置选‘a’的所有情况数(这些都是无效情况了,要减去),当只剩下a或者b的时候就直接输出对应的字母。
下面附上代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e6+10;
#define ll long long
ll dp[63][63];
int a,b;
ll k;
void pre() {
for(int i=0;i<=61;i++) {
dp[i][0]=1;
}
for(int i=1;i<=60;i++) {
for(int j=1;j<=i;j++) {
dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
}
}
}
int main() {
pre();
cin>>a>>b>>k;
int n=a+b;
for(int i=1;i<=n;i++) {
if(a==0) {
while(b) {
cout<<'b';
b--;
}
return 0;
}
if(b==0) {
while(a) {
cout<<'a';
a--;
}
return 0;
}
if(k<=dp[n-i][a-1]) {
a--;
cout<<"a";
}
else {
b--;
cout<<"b";
k-=dp[n-i][a-1];
}
}
return 0;
}
E题涉及启发式合并,暂时没学,学完这个算法后再来补
F涉及凸包算法
现在会的算法还太少,最后两个题设计的算法,还不太会,先学再补。
To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激