【1995提高】灯的排列问题 |
Time Limit:1000MS Memory Limit:65536K
Total Submit:731 Accepted:366
Description
设在一排上有N个格子(N≤20),若在格子中放置有不同颜色的灯,每种灯的个数记为N1,N2,……Nk(k表示不同颜色灯的个数)。(颜色数<4)
放灯时要遵守下列规则:
①同一种颜色的灯不能分开;
②不同颜色的灯之间至少要有一个空位置。
例如:N=8(格子数)
R=2(红灯数)
B=3(蓝灯数)
放置的方法有: R-B顺序,B-R顺序, 放置的总数为12种。
程序要求:求出排列方案总数。
Input
数据输入的方式为:
N
P1(颜色,为一个字母) N1(灯的数量)
P2 N2
……
Q(结束标记,Q本身不是灯的颜色)
Output
排列方案总数
Sample Input
8
R 2
B 3
Q
Sample Output
12
Source
ymh
【解析】
这个题开始想的很复杂,想要一下子出解。
先简化题目:相同颜色的灯必须放在一起,不同颜色的灯之间必须至少空一个格子,求排列总数。因为相同的灯必须放在一起,那么可以把每种灯都看做只有一个,将剩余部分从总格子数里面减掉。这样就可以看做是将m个不同颜色的灯放入n个格子里,要求任意两个灯不可相邻,求排列数。
本题的数据很弱,n<=20,颜色数<4。因此这样思考,先不考虑灯的颜色,只要求每个灯不相邻。可以用简单的搜索得出方案数,将这个方案数乘以颜色数的全排列数,即为本题答案。
我在想,如果本题的数据规模很大,搜索加上剪枝也会超时的话,有没有其他的方法来算。
可以这么来做,依然是先不考虑灯的颜色,把所有的灯先放成一排,然后往里“插空”。插空的时候要求灯和灯之间必须至少有一个空,而最左端和最右端可以没有空。
设最左端至最右端插的空数分别为 X1,X2,X3......Xm+1,其中X1,Xm+1>=0,X2,X3...Xm>0(由X均为整数可知X2,X3...Xm>=1);
令Y1=X1,Y2=X2-1,Y3=X3-1...Ym=Xm-1,Ym+1=Xm+1,其中 Y1,Y2...Ym+1>=0;
由X1+X2+...+Xm+1=n-m 可得 Y1+Y2+...Ym+1=n-m+m-1=n-1;
这样一来,问题就转化为求不定方程非负整数解的个数的问题,根据组合数学的相关知识,可以知道解的个数为C(n-1+m+1-1,m+1-1),即 C(n+m-1,m)。
知道这个以后,再乘以颜色数的全排列,即为答案了。
注意,算组合数的时候,应用分解质因数的方法,把分母约掉,将分子上的因数乘起来即可。
我只写了第一种,搜索也未加任何优化,这样也是0MS过掉的,可见数据之弱。
程序如下:var a:array[1..53] of longint; n,count,t,k,i:longint; ans:int64; s,c:string; procedure try(l,x:longint); var i:longint; begin if l=count then begin inc(ans); exit; end; for i:=x+2 to n do try(l+1,i); end; function fact(key:longint):int64; var i:longint; begin fact:=1; for i:=2 to key do fact:=fact*i; end; begin readln(n); while true do begin readln(s); if s='Q' then break; c:=copy(s,1,pos(' ',s)-1); t:=ord(c[1])-ord('A')+1; delete(s,1,pos(' ',s)); val(s,k); inc(a[t],k); inc(count); end; for i:=1 to 53 do begin dec(a[i]); if a[i]>0 then dec(n,a[i]); end; ans:=0; for i:=1 to n do try(1,i); ans:=ans*fact(count); writeln(ans); end.