King's Order
数位dp,三维数组 d[i][j][k]为处理完 i 个字符 , 结尾字符为 ′a′+j , 结尾部分已重复出现了 k 次的方案数。转移时当k>=2时,时,d[i][j][k]=d[i-1][j][k-1].
k=1时,d[i][j][1]将d[i-1]中各个字母结尾的各种长度加起来即可。
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3
const int N=30;
const int mod=1e9+7;
int d[2001][27][4],n;
int main(){
int t;
cin>>t;
memset(d, 0, sizeof(d));
for (int i=1; i<=26; i++) {
d[1][i][1]=1;
}
for (int i=2; i<=2000; i++) {
for (int j=1; j<=26; j++) {
for (int k=1; k<=26; k++) {
if (k==j) continue;
for (int g=1; g<=3&&g<=i; g++) {
d[i][j][1]+=d[i-1][k][g];
d[i][j][1]%=mod;
}
}
for (int k=2; k<=3&&k<=i; k++) {
d[i][j][k]=d[i-1][j][k-1];
}
}
}
while (t--) {
scanf("%d",&n);
int sum=0;
for (int i=1; i<=26; i++) {
for ( int j=1; j<=3; j++) {
sum+=d[n][i][j];
sum%=mod;
}
}
printf("%d\n",sum);
}
return 0;
}
King's Game
裸的约瑟夫是怎么玩的:n 个人,每隔 k 个删除。
由于我们只关心最后一个被删除的人,并不关心最后的过程,所以,我们没有必要也不能够模拟整个过程。我们用递推解决。假设有n个人围成环,标号为[0,n−1]从0开始的好处是取模方便),每数k个人杀一个的情况下,最后一个存活的人的编号是f[n]。
我们有f[1]=0,这不需要解释。
接着考虑一般情况f[n],第一个杀死的人的编号是k−1,杀死后只剩下n−1个人了,那么我们重新编号!
原来编号为k的现在是0号,也就是编号之间相差3。我们只要知道现在n−1个人的情况最后是谁幸存也就知道n个人的情况是谁幸存。幸运的是f[n−1]已经算出来了那f[n]就是在f[n−1]的基础上加上一个k即可不要忘记总是要取模。
所以递推式子是: f[i]={ 0 i=1 (f[i - 1] + k) mod i other
此题只用在原版约瑟夫问题上加一维,由于依次隔 1,2,3...n−1 个人删除,所以用 f[i][j] 表示 i 个人,依次隔 j,j+1...j+i−1 个人的幸存者标号。
根据刚才的重标号法,第一次 j−1 号出局,从 j 开始新的一轮,从 j+1 开始清除,剩余 i−1 个人,也有递推式子:
f[i][j]={ 0 i=1 (f[i - 1][j+1] + j) mod i other
答案就是 f[n][1]+1(将标号转移到 [1,n]),问题轻松解决。
题意已经说明的很清楚了,解法也很清楚,但题解说的滚动数组就没有悟到,而开5000^2数组爆了内存。后来发现有不用滚动数组的解法,就借来了。具体其实就是一个一个算,并不需要二维数组。学习了一个约瑟夫问题。
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3
const int N=30;
const int mod=1e9+7;
int f[5005],ans[5005],n;
int solve(int n){
f[1]=0;
int k=n-1;
for (int i=2; i<=n; i++) {
f[i]=(f[i-1]+(k--))%i;
}
return f[n]+1;
}
int main(){
int t;
cin>>t;
memset(f, 0, sizeof(f));
ans[0]=0;
ans[1]=1;
for (int i=2; i<=5000; i++) {
ans[i]=solve(i);
}
while (t--) {
scanf("%d",&n);
cout<<ans[n]<<endl;
}
return 0;
}