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 }
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]; (当前这个人分别用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 }
View Code DP+压位高精度

 

以上是关于noi 2.6_9288&hdu 1133Buy the Ticket(DP / 排列组合 Catalan+高精度)的主要内容,如果未能解决你的问题,请参考以下文章

noi 2.6_9289&bzoj2023 / 1630Ant Counting 数蚂蚁{Usaco2005 Nov}

noi 2.6_9280&bzoj 1089严格n元树(DP+重载运算符)

noi 2.6_90滑雪(DP)

noi 2.6_747Divisibility(DP)

noi 2.6_6045开餐馆(DP)

noi 2.6_9271奶牛散步(DP)