poj1173 多重集组合数

  这道题的本质是将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;
}

 

posted @ 2016-02-24 14:11  xing-xing  阅读(846)  评论(0编辑  收藏  举报