noi 2.6_9288&hdu 1133Buy the Ticket(DP / 排列组合 Catalan+高精度)
Posted konjac蒟蒻
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了noi 2.6_9288&hdu 1133Buy the Ticket(DP / 排列组合 Catalan+高精度)相关的知识,希望对你有一定的参考价值。
题意:有m个人有一张50元的纸币,n个人有一张100元的纸币。他们要在一个原始存金为0元的售票处买一张50元的票,问一共有几种方案数。
解法:(学习了他人的推导后~)
1.Catalan数的应用7的变形。(推荐阅读:http://www.cnblogs.com/chenhuan001/p/5157133.html)。P.S.不知我之前自己推出的公式“C(n,m)*C(2*m,m)/(m+1)*P(n,n)*P(m,m)”是否是正确的。
(1)在不考虑m人和n人本身组内的排列时,总方案数为C(m+n,n)或C(m+n,m)。
(2)而不合法的设1为50元(m个),0为100元(n个)。那么便有10110...设前2k+1个数中有k+1个0和k个1,因此不合法。而将2k+1之后的所有0(n-k-1个)和1(m-k个)异或。这样的转换是唯一对应的,不合法的方案数仍不会改变,因此可行,得到n-k-1个1和m-k个0。总共n-1个1和m+1个0。这样不合法的方案数就可用C(n+m,n-1)或C(n+m,m+1)表示了。
由于(1)-(2)已经包含了m人和n人跨组之间的组合,于是只乘上m人和n人本身组内的排列P(m,m)和P(n,n)表示答案,而不是P(n+m,n+m)。化简可得(m+n)! (m-n+1)/(m+1)。
2.隔板法。(不知道这个方法是不是这么叫......)
(1)先放了n个5元,构成n+1个空位。再一个个放m个100元,第一个,除了第1个空不可,其它位置都可放,合法的概率为n/(n+1)。第二个100元,概率变为(n-1)/n。以此类推,第m个的概率就是(n-m+1)/(n-m+2)。那么这m个100元的合法的总概率就是所有的概率相乘,得到(n-m+1)/(n+1)。
(2)然后再考虑这n+m个的排列方法P(n+m,n+m),与原来的相乘。
最终得到(n+m)!*(n-m+1)/(n+1).
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 #define N 110 7 8 struct node 9 { 10 int s[510]; 11 int l; 12 node() {l=0;memset(s,0,sizeof(s));} 13 }; 14 node multi(node x,int y)//dif from + 15 { 16 node z; 17 z.l=x.l; 18 for (int i=1;i<=x.l;i++) 19 { 20 z.s[i]+=x.s[i]*y; 21 if (z.s[i]>9) z.s[i+1]+=z.s[i]/10,z.s[i]%=10; 22 } 23 while (z.s[z.l+1]) 24 { 25 z.l++; 26 if (z.s[z.l]>9) z.s[z.l+1]+=z.s[z.l]/10,z.s[z.l]%=10; 27 } 28 return z; 29 } 30 node conv(node x) 31 { 32 node z; 33 z.l=x.l; 34 for (int i=1;i<=x.l;i++) 35 z.s[x.l-i+1]=x.s[i]; 36 return z; 37 } 38 node div(node x,int y) 39 { 40 node z; 41 int t=x.l+1,h=0; 42 while (h<y && t>1) h=10*h+x.s[--t]; 43 z.s[++z.l]=h/y,h%=y; 44 while (t>1) 45 { 46 h=10*h+x.s[--t]; 47 z.s[++z.l]=h/y,h%=y; 48 } 49 z=conv(z); 50 return z; 51 } 52 /*另一种打法 53 node div(node x,int y)//crucial 54 { 55 node z; 56 int t=x.l+1,h=0; 57 while (t>1)//converse! 58 { 59 bool tf=true; 60 while (h<y && t>1) 61 { 62 h=10*h+x.s[--t]; 63 if (!tf&&z.l) z.s[++z.l]=0;//商最高位之后才开始赋值0,并且要是加入新的一位为0 64 tf=false; 65 } 66 z.s[++z.l]=h/y,h%=y; 67 } 68 z=conv(z); 69 return z; 70 } 71 */ 72 void print(node x) 73 { 74 for (int i=x.l;i>=1;i--) 75 printf("%d",x.s[i]); 76 printf("\\n"); 77 } 78 node P(int x) 79 { 80 node z; 81 z.l=z.s[1]=1; 82 for (int i=x;i>=1;i--) 83 z=multi(z,i); 84 return z; 85 } 86 int main() 87 { 88 int n,m; 89 while (1) 90 { 91 scanf("%d%d",&n,&m); 92 if (!n&&!m) break; 93 if (n<m) {printf("0\\n");continue;} 94 print(div(multi(P(n+m),n-m+1),n+1)); 95 } 96 return 0; 97 }
3.动态规划。转载自:http://blog.csdn.net/clover_hxy/article/details/52931233
f[i][j]表示到第i个人,已经有j个人用50元买了门票。
f[i][j] = f[i-1][j] + f[i-1][j-1]; (当前这个人分别用100元或50元。同时用 k=i-j 表示100元的个数,递推时保证 k<=j。)
因为每个人是不一样的所以最后的答案是 f[n+m][n]*n!*m!。
P.S.看!DP一个状态递推就能省了上面2种一长串的头脑风暴推导过程,可见DP的优越性。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #define N 103 7 using namespace std; 8 struct data{ 9 int num[403]; 10 }f[N*2][N],ans; 11 int n,m,a[N],b[N],c[N]; 12 int calc(int a[N],int x) 13 { 14 a[0]=1; a[1]=1; 15 for (int i=1;i<=x;i++) 16 { 17 for (int j=1;j<=a[0];j++) 18 a[j]*=i; 19 for (int j=1;j<=a[0];j++) { 20 a[j+1]+=a[j]/10000; 21 a[j]%=10000; 22 } 23 int t=a[0]; 24 while (a[t+1]) { 25 t++; 26 a[t+1]+=a[t]/10000; 27 a[t]%=10000; 28 } 29 a[0]=t; 30 } 31 } 32 void add(data &x,data y,data z) 33 { 34 int t=max(y.num[0],z.num[0]); 35 for (int i=1;i<=t;i++) 36 x.num[i]=y.num[i]+z.num[i]; 37 for (int i=1;i<=t;i++) 38 x.num[i+1]+=x.num[i]/10000,x.num[i]%=10000; 39 while (x.num[t+1]){ 40 t++; 41 x.num[t+1]+=x.num[t]/10000; 42 x.num[t]%=10000; 43 } 44 x.num[0]=t; 45 } 46 void mul(data &x,int a[N]) 47 { 48 memset(c,0,sizeof(c)); 49 for (int i=1;i<=x.num[0];i++) 50 for (int j=1;j<=a[0];j++) 51 c[i+j-1]+=x.num[i]*a[j]; 52 int t=max(x.num[0],a[0]); 53 for (int i=1;i<=t;i++) 54 c[i+1]+=c[i]/10000,c[i]%=10000; 55 while(c[t+1]) { 56 t++; 57 c[t+1]+=c[t]/10000; 58 c[t]%=10000; 59 } 60 c[0]=t; 61 for (int i=0;i<=c[0];i++) x.num[i]=c[i]; 62 } 63 int main() 64 { 65 freopen("a.in","r",stdin); 66 freopen("my.out","w",stdout); 67 int n=100; int m=100; 68 f[0][0].num[0]=1; 69 f[0][0].num[1]=1; 70 for (int i=1;i<=n+m;i++) 71 for (int j=min(i,n);j>=1;j--) 72 { 73 int k=(i-j); 74 if (k>j) break; 75 add(f[i][j],f[i-1][j],f[i-1][j-1]); 76 } 77 int cnt=0; 78 while (true){ 79 scanf("%d%d",&n,&m); cnt++; 80 if (n==0&&m==0) break; 81 memset(a,0,sizeof(a)); 82 memset(b,0,sizeof(b)); 83 calc(a,n); calc(b,m); 84 for (int i=0;i<=400;i++) ans.num[i]=0; 85 for (int i=0;i<=f[n+m][n].num[0];i++) 86 ans.num[i]=f[n+m][n].num[i]; 87 mul(ans,a); mul(ans,b); 88 printf("Test #%d:\\n",cnt); 89 if (ans.num[0]==0||n<m) { 90 printf("0\\n"); 91 continue; 92 } 93 for (int i=ans.num[0];i>=1;i--){ 94 int t=ans.num[i]; 95 if (i!=ans.num[0]){ 96 if (t<10) printf("000"); 97 else if (t<100) printf("00"); 98 else if (t<1000) printf("0"); 99 } 100 printf("%d",t); 101 } 102 printf("\\n"); 103 } 104 }
以上是关于noi 2.6_9288&hdu 1133Buy the Ticket(DP / 排列组合 Catalan+高精度)的主要内容,如果未能解决你的问题,请参考以下文章
noi 2.6_9289&bzoj2023 / 1630Ant Counting 数蚂蚁{Usaco2005 Nov}