这道题的本质是将n个物品分成k堆, 每堆物品个数大于0小于等于m的方案数。 我们定义d[i][j]为前i堆物品总数为j的方案数, 那么d[i][j]的求解方法如下:其可化为d[i][j] = d[i][j-1] + d[i-1][j-1]+d[i-1][j-1-m]. 初始条件为d[0][0] = 1; d[i][0] = 0; (i!=0) 证明如下:
对于第二问已知一个串求它的序号我们可以讲他写成len向量,然后统计比他小的个数, 比如1101100 -> 2122可知当序号为偶数的时候他是1 奇数的时候是0, 对于1我们减少第一个1右面1的个数, 对于0我们将0拓展到它的右面。 比如比2122小的可以分为4部分:
1??? 23?? 22?? 211? 代码如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; int n, k, m; int d[50][50]; //前i堆有n个物品的方案数 int s; char str[100][50]; int getid(int s) //获取第s个字符串的id { int tp[50], ntp=0; int last = 1; for(int i=1; i<n; i++) { if(str[s][i] != str[s][i-1]) tp[ntp++]=last, last=0; last++; } if(last!=0) tp[ntp++] = last; int res = 0; int u = n; for(int i=0; i<ntp-1; i++) { if(i%2==0) // 1 { for(int j=1; j<tp[i]; j++) if(u>=j) res += d[k-i-1][u-j]; } else // 0 { for(int j=m; j>tp[i]; j--) if(u>=j) res += d[k-i-1][u-j]; } u -= tp[i]; } return res; } int main() { cin>>n>>k>>m; d[0][0] = 1; for(int i=1; i<=k; i++) for(int j=1; j<=n; j++) { d[i][j] = d[i][j-1] + d[i-1][j-1]; if(j-m-1>=0) d[i][j] -= d[i-1][j-m-1]; } int res1 = d[k][n]; cin>>s; for(int i=0; i<s; i++) cin>>str[i]; cout<<res1<<endl; for(int i=0; i<s; i++) { int res2 = getid(i); cout<<res2<<endl; } return 0; }