题意:
给出10个数字,分别表示数i有a[i]个,问这些数都要用上能构成被11整除的数的个数。
题解:
初始状态dp[i][j][x][y] 用前i个数,j位奇数位,奇数位和为x,偶数位和为y满足条件的个数。很明这个内存不允许,其实可以把偶数为的状态删掉,因为偶数为的个数可以用总位数-奇数位的,只要没次记录总位数。变成dp[i][j][x]但是还是太大,用滚动数组dp[2][j][x],然后dp完,枚举奇数位的和得到答案。
这是钟神把BNU 11993 改编的,题目挺好的。
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const ll MOD=10007;
const int maxn=105;
const int maxm=1005;
int num[10];
ll dp[2][105][1005];
ll C[1005][1005];
void Init()
{
for(int i=0;i<=1000;i++)
{
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
}
}
int main()
{
Init();
int T,sum,cnt;
scanf("%d",&T);
for(int cas=1;cas<=T;cas++)
{
sum=cnt=0;
for(int i=0;i<=9;i++)
{
scanf("%d",&num[i]);
cnt+=num[i];
sum+=num[i]*i;
}
memset(dp,0,sizeof dp);
dp[0][0][0]=1;
int now=0,pre=1;
int tol=0;
ll temp;
for(int dig=9;dig>=0;dig--)
{
now^=1;
pre^=1;
tol+=num[dig];
memset(dp[now],0,sizeof dp[now]);
for(int i=0;i<=100;i++)
{
for(int j=0;j<=1000;j++)
if(dp[pre][i][j])
{
for(int a=0;a<=num[dig];a++)
{
temp=dp[pre][i][j];
int da=i+a;
int b=num[dig]-a;
int db=tol-da;
if(dig==0)
{
if(i==0)
temp=0;
else
temp=(temp*C[da-1][a])%MOD;
temp=(temp*C[db][b])%MOD;
}
else
{
temp=(temp*C[da][a])%MOD;
temp=(temp*C[db][b])%MOD;
}
dp[now][i+a][j+dig*a]=(dp[now][i+a][j+dig*a]+temp)%MOD;
}
}
}
}
ll ans=0;
int dig=(cnt-cnt/2);
for(int i=0;i<=sum;i++)
{
int j=sum-i;
int t=abs(i-j);
if(t%11==0)
ans=(ans+dp[now][dig][i])%MOD;
}
printf("Case %d: ",cas);
cout<<ans<<endl;
}
return 0;
}
/***
*/