有n种物品, 第i只能怪物品有a[i]个。不同种类的 物品可以相互区分,但同一种类的物品无法相互区分,从中取出m个的话,求取的方案数。
题源来自《挑战程序竞赛》第二版 68页。
根据其定义,有如下DP数组:
dp[i+1][j]=从前i个物品中取出j个的组合数。
为了从前i个物品中取出j个,可以这样实现:从前i-1种物品取出j-k种,再从第i种物品中取出k个,所以,有以下递推式:
dp[i+1][j]=对k求和 (dp[i][j-k])
复杂度为O(nm^2);
注意到:
对k求和(dp[i][j-k])= 对k求和(dp[i][j-1-k]) + dp[i][j]-dp[i][j-1-a(i)] (即一头一尾)
对应的就是:
dp[i+1][j]=dp[i+1][j-1]+dp[i][j]-dp[i][j-1-a(i)]
复杂度为O(nm)。
貌似书中code初始化有问题,改了下,运行记过正确。
//
// 068_multi set sum.cpp
// changlle
//
// Created by user on 12/29/15.
// Copyright (c) 2015 user. All rights reserved.
//
#include <iostream>
using namespace std;
int n=3;
int m=3;
int a[3]={1,2,3};
int main() {
int dp[4][4];//n+1 m+1 前i种取出j个的组合
for (int j=1;j<=m;j++)
dp[0][j]=0; //一个都不取 的方案只有一种
for (int i=0;i<=n;i++)
dp[i][0]=1; //一个物品也没有的时候,取的方案都为0
for (int i=0;i<n;i++)
for (int j=1;j<=m;j++)
{
if (j-1-a[i]>=0)
dp[i+1][j]=dp[i+1][j-1]+dp[i][j]-dp[i][j-1-a[i]];
else
dp[i+1][j]=dp[i+1][j-1]+dp[i][j];
}
for (int i=0;i<=n;i++){
for (int j=0;j<=m;j++)
cout<<dp[i][j]<<" ";
cout<<endl;
}
cout<<dp[n][m]<<endl;
return 0;
}