N球M盒问题 & Password Attacker google code jam

  第一个题目本质是个动归dp问题。但是分析dp的方程使用了组合数学中的N求M盒问题。

先来看下N球M盒的经典问题。

给定N个相同的球,放入M个不同的盒子中,要求每个盒子必须非空,求组合数。

假设Xi为第i个盒子中的放入的小球数,则方程

X1+X2+X3+...+Xm=N,

其实抽象成数学问题,就是求这个M元1次方程的正整数解的组数。

插板法分析:

N个小球排成一行,插入M-1个板,只能在小球中间插,而且板与板之间必须有球

分析到这里,聪明的大家肯定一看就知道是个N-1个空隙选M-1个来放入板,Cm-1,n-1.

有了上面的分析方法后,我们可以接着来分析原题。

dp[i][j]可以先考虑dp[i-1][k],

那么还剩下j-k个相同的元素待插入,把j-k相同的球分成k个不同的堆,每堆可以为空。

为了转化成堆至少有一个球的情形,我们每个堆放入一个球,现在又可以变成了插板问题。最后插板得到的组合,每个减去1就是了

所以:最后动归的公式就是

dp(i,j)=sum( dp(i-1,k)C(j,k))

k 是 i-1 ~ j-1

 

调试的过程中一定用longlong来存储

typedef int64 long long 

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<iomanip>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<string>
  8 #include<sstream>
  9 #include<vector>
 10 #include<map>
 11 #include<set>
 12 #include<bitset>
 13 #include<algorithm>
 14 #include<cassert>
 15 #include<ctime>
 16 #include<queue>
 17 using namespace std;
 18 
 19 #define REP(i,n) for(int i = 0; i < (int)n; i++)
 20 #define FOR(i,n,m) for(int i = (int)n; i <= (int)m; i++)
 21 #define FOD(i,n,m) for(int i = (int)n; i >= (int)m; i--)
 22 #define EACH(i,v) for(decltype((v).begin()) i = (v).begin(); i != (v).end(); i++)
 23 
 24 typedef long long i64;
 25 typedef pair<int, int> PI;
 26 
 27 #define sz(v) ((i64)(v).size())
 28 #define all(v) (v).begin(),(v).end()
 29 #define bit(n) (1LL<<(i64)(n))
 30 
 31 #define PB push_back
 32 #define MP make_pair
 33 #define X first
 34 #define Y second
 35 template<class T> void fmax(T &a, const T &b) { if (a < b) a = b; }
 36 template<class T> void fmin(T &a, const T &b) { if (a > b) a = b; }
 37 template<class T> T sqr(const T &a) { return a * a; }
 38 
 39 long long M=1000000007LL;
 40 
 41 long long C[101][101];
 42 long long  dp[101][101];
 43 
 44 void composition(){
 45     FOR(i,0,100){
 46         C[i][i]=1;
 47         C[i][0]=1;
 48     }
 49 
 50     FOR(i,2,100){
 51         FOR(j,1,i-1){
 52             C[i][j]=C[i-1][j]+C[i-1][j-1];
 53         }
 54     }
 55 }
 56 
 57 void getCombination() {
 58     int n = 105;
 59     for (int i = 1; i <= n; i++)
 60         C[i][0] = C[i][i] = 1LL;
 61 
 62     for (int i = 2; i <= n; i++)
 63     for (int j = 1; j <= i; j++)
 64         C[i][j] = (C[i-1][j-1] % M + C[i-1][j] % M) % M;
 65 }
 66 
 67 long long  solve(int m,int n){
 68     /*
 69     memset(dp,0,sizeof(dp));
 70     FOR(i,1,n)
 71         dp[1][i]=1;
 72     FOR(i,2,n)
 73         dp[i][i]=(dp[i-1][i-1] * (long long)i ) %M;
 74 
 75     FOR(i,2,m){
 76         FOR(j,i+1,n){
 77             FOR(k,i-1,j-1){
 78                 //dp[i][j]=(dp[i][j]+(dp[i-1][k]*C[j][k])%M)%M;
 79                 dp[i][j] = (dp[i][j] + (C[j][k] * dp[i-1][k]) % M) % M;
 80             }
 81         }
 82     }
 83     */
 84     memset(dp, 0LL, sizeof(dp));
 85         for (int i = 1; i <= n; i++)    dp[1][i] = 1;
 86         for (int i = 2; i <= n; i++)    dp[i][i] = (dp[i-1][i-1] * (long long)i) % M;
 87         
 88         for (int i = 2; i <= m; i++)
 89         for (int j = i+1; j <= n; j++)
 90         for (int k = i-1; k <= j-1; k++)
 91             dp[i][j] = (dp[i][j] + (C[j][k] * dp[i-1][k]) % M) % M;
 92     return dp[m][n];
 93 }
 94 
 95 int main(){
 96     freopen("input.txt","r",stdin);
 97 //    freopen("out.txt","w",stdout);
 98     int M,N,T;
 99     //composition();
100     getCombination();
101     cin>>T;
102     int counter=1;
103     while(T--){
104         cin>>M>>N;
105         cout<<"Case #"<<counter++<<": "<<solve(M,N)<<endl;
106         
107     }
108     return 0;
109 }
View Code

 

 

最后代码奉上

 

posted on 2014-10-12 15:36  ACKing  阅读(267)  评论(0编辑  收藏  举报

导航