标签:dp
题意:给一个数字串,可以调换数字,问有多少种组合可以让组成的数能被11整除。
思路:窝们观察到1%11=1, 10%11=10,100%11=1,1000%11=10,以此类推。。窝们将一偶一奇看作一对,这一对组成对11的余数
×100对11的余数(也就是1),所以实质还是这一对对11的余数,那么奇偶数位的和就可以了。我们可以设奇数位的和为x,偶数位的
和为y,则(x+10y)%11的值为0就可以了--> (x-y+11y)%11=0 --> (x-y)%11=0。所以设dp[i][j][k]表示处理完0到i-1,已在奇数位放了j个
数字,(奇数位数字和-偶数位数字和)%11=k的种数,详见代码:
/********************************************************* file name: LA6529.cpp author : kereo create time: 2015年02月04日 星期三 20时39分19秒 *********************************************************/ #include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<set> #include<map> #include<vector> #include<stack> #include<cmath> #include<string> #include<algorithm> using namespace std; typedef long long ll; const int sigma_size=26; const int N=15; const int MAXN=100+10; const int inf=0x3fffffff; const double eps=1e-8; const int mod=1000000000+7; #define L(x) (x<<1) #define R(x) (x<<1|1) #define PII pair<int, int> #define mk(x,y) make_pair((x),(y)) char str[MAXN]; int num[N]; ll C[MAXN][MAXN]; ll dp[N][MAXN][N];//dp[i][j][k]表示处理完0到i-1,已在奇数位放了j个数字,(奇数位数字和-偶数位数字和)%11=k的种数 void init(){ for(int i=0;i<MAXN;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; } } ll solve(int len){ memset(dp,0,sizeof(dp)); dp[0][0][0]=1; int sum=0,n=(len+1)>>1; for(int i=0;i<10;i++){ //处理的数位 for(int j=0;j<=sum && j<=n;j++){ //已放在奇数位的个数 for(int k=0;k<=num[i] && k<=n-j;k++){ //这次在奇数位放的个数 int x=((2*k-num[i])*i)%11; if(x<0) x+=11; for(int st=0;st<=10;st++){ dp[i+1][j+k][(st+x)%11]=((((dp[i][j][st]*C[n-j][k])%mod)*C[len-sum-(n-j)][num[i]-k])%mod+dp[i+1][j+k][(st+x)%11])%mod; } } } sum+=num[i]; } return dp[10][n][0]; } int main(){ init(); while(~scanf("%s",str)){ memset(num,0,sizeof(num)); int n=strlen(str); for(int i=0;i<n;i++) num[str[i]-'0']++; ll ans1=solve(n),ans2=0; if(num[0]){ //剔除前导零的情况 num[0]--; ans2=solve(n-1); } printf("%lld\n",((ans1-ans2)%mod+mod)%mod); } return 0; }
标签:dp
原文地址:http://blog.csdn.net/u011645923/article/details/43538107