题目:http://acm.hdu.edu.cn/showproblem.php?
pid=5389
题意:定义数根:①把每一位上的数字加起来得到一个新的数,②反复①直到得到的数仅仅有1位。给定n,A,B和n个一位数,求把这n个数分成两部分,使得这两部分的当中一部分的和的数根等于A另外一部分的和的数根等于B的方案数。
分析:一个数a的数根s=(a-1)%9+1,为了方便直接用s=a%9,当中0代表9。定义dp[i][j]表示前i个数中数根为j的方案数。
对于第i个数能够选也能够不选,那么状态转移方程为dp[i][j]=dp[i-1][j](不选),dp[i][(j+c[i])%9]+=dp[i-1][j](选)。特殊情况就是{i:1~n,sigma(c[i])}%9!=(A+B)%9,这样的情况不能将这n个数划分成非空的满足条件的两部分,须要特判一下。
代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 1e5+7; const int mod = 258280327; int dp[maxn][10],c[maxn]; int main() { int ncase,n,A,B,i,j; scanf("%d",&ncase); while(ncase--) { scanf("%d%d%d",&n,&A,&B); int sum=0; for(i=1;i<=n;i++) { scanf("%d",&c[i]); sum+=c[i]; } if(sum%9!=(A+B)%9) { if(A%9==sum%9 && B%9==sum%9) printf("2 "); else if(A%9==sum%9 || B%9==sum%9) printf("1 "); else printf("0 "); continue ; } dp[0][0]=1; for(i=1;i<=n;i++) { for(j=0;j<=8;j++) dp[i][j]=dp[i-1][j]; for(j=0;j<=8;j++) dp[i][(j+c[i])%9]=(dp[i][(j+c[i])%9]+dp[i-1][j])%mod; } printf("%d ",dp[n][A%9]); } return 0; }