题解: 建筑物
题目:
有R红色立方体,G绿色立方体和B蓝色立方体。每个立方体的边长是1。现在有一个N × N的木板,该板被划分成1×1个单元。现在要把所有的R+G+B个立方体都放在木板上。立方体必须放置在单元格内,单元格可以竖立放置多个立方体。
放置在板上的立方体可以被视为“建筑物”。一个“建筑物”被称为“美丽建筑物”,当且仅当:人站在南面,向北面望过去,观察建筑物时,所有可见立方体都是相同的颜色。
例如,在下图中,左侧建筑物是“美丽建筑物”,而右侧建筑物则不是。
问题是:给出R,G,B,N,有多少种不同的“美丽建筑物”,答案模1000000007。
多组测试数据。
第一行,一个整数group。表示有group组测试数据。1 <= group <= 8
每组测试数据格式:
一行,4个整数:R,G,B,N。 0<=R,G,B<26。 1 <= N <26。
题目大意:有红、蓝、绿三种颜色的正方体分别R,B,G个,放在一个N × N的平面,高度不限,使得在一个固定的方向看(在这里我们简单化为在前看),看见的所有格子颜色相同!
题解:
总体思路:
它有N行,我们可以化成小问题:第N行的放法由第N-1行推出:设第N-1行用了R1,B1,G1个红、蓝、绿方块,第N行只能用R-R1,B-B1,G-G1个红、蓝、绿方块,这时第N行的放法=第N-1行的放法*(R-R1,B-B1,G-G1个红、蓝、绿方块在一列的放法) *这时需保证这一行的放法在前看的颜色与N-1行相同
这是近一步设数组f[i][r][b][g],i为第i行、r,b,g为到这一行(包括这一行)用的红、蓝、黄的块数(r<=R,b<=B,g<=G),g[r][b][g]:用r,b,g块红、蓝、黄正方体放在一列的放法。可转化为:f[i][r][b][g]=f[i-1][r1][b1][g1]*g[r-r1][b-b1][g-g1](r1<=r,b1<=b,g1<=g)*为保证当前行的放法在前看的颜色与前面的行相同,数组f为在前面看颜色为r的放法数,数组g为在前面看颜色为r的放法数!!!
结果为f[N][R][B][G]+f[n][B][R][G]+f[N][G][R][B](其中数组后两维可以调换)
接下来,就是求g数组:
可以发现:求一列的方案数和之前的想法相似,设s[i][r][b][g] :为在某一列中到第i行,为到这一行(包括这一行)用的红、蓝、黄的块数为r,b,g。
得s[i][r][b][g]=s[i-1][r-r1][b-b1][g-g1]+(b1+g1+r1)!/b1!/r1!/g1!(r1<=r,b1<=b,g1<=g),g[r][b][g]=s[n][r][b][g]
但是,我们发现,为这时如果b1+g1+r1大于之前的最高高度时,没法保证露在外面的部分颜色为r。所以我们要在s数组的基础上多一维分别表示:限定的最高的高度。
同时动态转移方程便变得分多种情况考虑:分r+b+g<=h、r+b+g>h
优化:
上面的解法时间复杂度为O(25^8),远远超过五秒时限。造成这样的原因,主要是因为枚举r,b,g,r1,b1,g1,为什么我们不妨先把它们看做一个同一个整体(即同一个颜色),先求出s数组,但在求g数组时因为我们把其看成了一种颜色,所以用组合数求出其方案数。
这时候我们需要把之前数组s[h][n][z],h即最高高度,n即为到了第n行,z即为三总颜色块数的总数。g[G][Y]即放G个绿色的和Y个红或黄的在一列的放法。
但是在算某个方案的实际方案数时,需知道它的最高高度H,在总共N-H个,选R-H,R即可以放在前面的方块的块数。但因为我们在s数组中不知道R,所以我们放在求g数组时求,这是发现我们s数组的漏洞:没有保证在最高高度是多少?(即H是多少?)我们在原先的数组中加多一维只有0,1来判断在s[h][n][z]的是否到达最高位。这是求g数组:g[G][Y]+=s[h][n][G+Y][1]*YHSJ[G+Y-h][G-h]*YHSJ[Y][R](YHSJ[i][j]即杨辉三角,求组合数:i个中选j个的方案数) 但是R怎么求?我们可以不考虑其,先把它等到f数组时再考虑。
f数组的定义改为:f[i][g][y]到第i行用分别g个绿、y个红或蓝的方案数,答案=f[N][G][R+B]*YHSJ[R+B][R],这时可以发现原来R可是不需要枚举去求,可以直接在输出时*YHSJ[R+B][R]。此时,f[i][g][y]+=f[i-1][g-g1][y-y1]*f[g1][y1]。
在最后算的时候,千万别只输出f[N][G][R+B]*YHSJ[R+B][R](这仅仅只是绿色在前面的方案数),所以输出f[N][B][R+G]*YHSJ[R+G][R]+f[N][G][R+B]*YHSJ[R+B][R]+f[N][R][G+B]*YHSJ[G+B][G]!!!
最后附上代码:
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 long long group,rR,gG,bB,n,s[26][26][76][2],q[26][55],ans[26][26][55],mod=1000000007; 5 long long yhsj[76][76],u; 6 void YHSJ() 7 { 8 yhsj[0][0]=1; 9 for (long long i=1;i<=75;i++) 10 { 11 for (long long j=0;j<=i;j++) 12 { 13 if (j==0)yhsj[i][j]=1; 14 else 15 { 16 yhsj[i][j]=(yhsj[i-1][j]+yhsj[i-1][j-1])%mod; 17 } 18 } 19 } 20 } 21 void Find(long long g,long long r,long long b,long long n) 22 { 23 long long y=r+b; 24 memset(q,0,sizeof(q)); 25 memset(ans,0,sizeof(ans)); 26 q[0][0]=1; 27 for (long long i=1;i<=g;i++) 28 for (long long j=0;j<=y;j++) 29 for (long long z=1;z<=i;z++) 30 { 31 q[i][j]=(q[i][j]+((s[z][n][i+j][1]*yhsj[i+j-z][i-z])%mod))%mod; 32 } 33 ans[0][0][0]=1; 34 for (long long i=1;i<=n;i++) 35 for (long long G=0;G<=g;G++) 36 for (long long Y=0;Y<=y;Y++) 37 for (long long gg=0;gg<=G;gg++) 38 for (long long yy=0;yy<=Y;yy++) 39 { 40 ans[i][G][Y]=(ans[i][G][Y]+((ans[i-1][G-gg][Y-yy]*q[gg][yy])%mod))%mod; 41 } 42 43 u=(u+(ans[n][g][y]*yhsj[y][b]%mod))%mod; 44 } 45 int main() 46 { 47 YHSJ(); 48 for (long long maxn=1;maxn<=25;maxn++) 49 { 50 s[maxn][0][0][0]=1; 51 for (long long i=1;i<=25;i++) 52 for (long long now=0;now<=maxn;now++) 53 for (long long h=now;h<=75;h++) 54 { 55 if (now==maxn) 56 { 57 s[maxn][i][h][1]=((s[maxn][i][h][1]+s[maxn][i-1][h-now][1]%mod)+s[maxn][i-1][h-now][0])%mod; 58 } 59 else 60 { 61 s[maxn][i][h][1]=(s[maxn][i][h][1]+s[maxn][i-1][h-now][1])%mod; 62 s[maxn][i][h][0]=(s[maxn][i][h][0]+s[maxn][i-1][h-now][0])%mod; 63 } 64 } 65 } 66 scanf("%lld",&group); 67 for (long long G=1;G<=group;G++) 68 { 69 u=0; 70 scanf("%lld%lld%lld%lld",&rR,&gG,&bB,&n); 71 Find(rR,gG,bB,n); 72 Find(gG,rR,bB,n); 73 Find(bB,rR,gG,n); 74 printf("%lld\n",u); 75 } 76 return 0; 77 }