首页 > 代码库 > 【noi 2.6_9288】&【hdu 1133】Buy the Ticket(DP / 排列组合 Catalan+高精度)

【noi 2.6_9288】&【hdu 1133】Buy 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)//crucial54 {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,并且要是加入新的一位为064         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 }
View Code 排列组合+高精度

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]; (f[i-1][j]表示当前这个人用100元,f[i-1][j-1]表示当前这个人用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 }
View Code DP+压位高精度

 

【noi 2.6_9288】&【hdu 1133】Buy the Ticket(DP / 排列组合 Catalan+高精度)