灯的排列问题

【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.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值