数位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; }
裸的约瑟夫是怎么玩的: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; }