[hdu4045]Machine scheduling (DP+第二类斯特林数)

传送门
(这题没明确讲多组数据害我WA了一发)

可以把题目分成两个部分
- 从n个机器中选出r个
- 将r个机器分成不超过m组

第二个子问题很明显是第二类斯特林数,即 mi=1Sr,i ∑ i = 1 m S r , i

比较棘手的是第一个子问题,网上的题解多是插板法不再赘述,这里提供一种DP的做法。
f[i][j] f [ i ] [ j ] 表示当前选了 j j 个数,且所选最大数不大于i的方案数
由于相邻元素的差不小于 k k ,显然最大数为i的方案数是 f[ik][j1] f [ i − k ] [ j − 1 ]
再加上最大数不大于 i1 i − 1 的方案数,所以可得 f[i][j]=f[ik][j1]+f[i1][j] f [ i ] [ j ] = f [ i − k ] [ j − 1 ] + f [ i − 1 ] [ j ]
这个子问题的答案就是 f[n][r] f [ n ] [ r ]

两个子问题答案相乘就是最终答案了。实现上还有些细节可以看代码。

这种做法的缺点在于没法预处理,对每组数据都要重新DP,数据组数多的话肯定会T的。
大家还是出门左转用组合数吧(组合数大法好)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
const int N=1001;
const int p=1e9+7;
LL n,r,k,m,s[N][N],f[N][N],ans;

void read(LL &x){
    char ch=getchar();x=0;
    for(;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
}

void add(LL &x,LL y){
    x+=y;
    while(x>=p) x-=p;
}

int main(){
    s[0][0]=1;
    for(int i=1;i<N;i++)
     for(int j=1;j<=i;j++)
      s[i][j]=(s[i-1][j]*j%p+s[i-1][j-1])%p;    //预处理斯特林数
    while(cin>>n>>r>>k>>m){
        //memset(f,0,sizeof f);
        for(int i=1;i<=n;i++){   //dp一波
            f[i][0]=1;f[i][1]=i;
            for(int j=2;j<=r;j++){
                if (i-k>0) f[i][j]=f[i-k][j-1];
                 else f[i][j]=0;
                add(f[i][j],f[i-1][j]);
            }
        }
        ans=0;
        for(int i=1;i<=m;i++) add(ans,s[r][i]);
        //cout<<ans<<endl;
        ans=ans*f[n][r]%p;
        //cout<<f[n][r]<<endl;
        cout<<ans<<endl;
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值