题目链接:点击打开链接
这题有点像二维的01背包,主要是想到怎么设置dp[i][j]的含义。因为这里涉及到排列数,有很多种可能,但是最后一个数都是只有从0~n-1这几种可能,前面的数字可以全排列,后面的数字全排列。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 55
using namespace std;
int n,v;
int a[maxn];
double f[maxn],dp[maxn][maxn];//dp[i][j]以第i结尾,占有j容量的种类数
//则结果为sum(dp[i][j]*f[j]*f[n-j-1]/f[n])
void init()
{
f[0]=f[1]=1;
for(int i=2;i<=50;i++)
{
f[i]=f[i-1]*i;
}
}
void DP()
{
double ret=0;
for(int i=0;i<n;i++)
{
//初始化
memset(dp,0,sizeof(dp));//没放东西的时候只有最后一个放a[i]这一种情况,其他情况设置为0还是有点不懂,表所有状态必须从dp[0][0]开始么?
dp[0][0]=1;
for(int l=0;l<n;l++)
{
if(l!=i)
{
for(int j=n-1;j>0;j--)//个数限制
{
for(int k=v;k>=a[l];k--)//容量限制
{
dp[j][k]+=dp[j-1][k-a[l]];//转移方程
}
}
}
}
for(int j=0;j<n;j++)
{
for(int k=0;k<=v-a[i];k++)
{
ret+=dp[j][k]*f[j]*f[n-j-1];
}
}
}
printf("%lf\n",ret/f[n]);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
scanf("%d",&v);
init();
DP();
return 0;
}