【递推】【NOIP模拟】彩灯的问题 Lights

本文介绍了如何解决NOIP比赛中关于彩灯设计的问题,通过递推算法来计算不同颜色方案。当有n盏彩灯和m种颜色时,需要设计相邻彩灯颜色不同的方案。通过分析相邻彩灯的关系,建立了递推公式f(n) = (m-2)*f(n-1) + (m-1)*f(n-2),并给出了初始条件f(1)、f(2)、f(3)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目叙述

为了庆祝2009NOIP的举办,X市决定举行一次规模宏大的联欢晚会。设计师Tom被邀请负责晚会的灯光设计。
晚会舞台的正上方有 n 盏可以任意变换颜色的彩灯。它们排列成规则的圆形。为了增加舞台的美感,Tom 决定将任意两盏相邻彩灯设计成不同的颜色。因为演出时还要随时变换彩灯的颜色,所以Tom必须设计出多种方案(只要有一盏对应的彩灯颜色不同,就算两种不同的方案)。
于是一个棘手的问题摆在Tom面前:若有m种颜色, n 个彩灯,那么不同的设计方案有多少种呢?因为当n, m 较大时,方案数太多,因此他需要你的帮助。
注意:因为每一盏彩灯的位置固定,所以经过旋转或翻转能重合的也算不同的方案。

输入

输入共有一行,两个数: n ,m,依次为彩灯的个数与颜色总数。 (1n100,1m100)

输出

输出仅有一个数为方案总数。

样例输入

3 4

样例输出

24


先给出自己的很麻烦的递推。
首先这道题一看就是递推
然后要怎么推呢?
简单来说,我们把灯断开拉成一条链,管他第一个灯在哪,就让他随便。
这样的话第一盏灯的颜色有 m 种情况。
对于每种情况分别计算,但是其实都是一样的,所以只需要计算其中一种然后m输出即可。
对于其中的一种,举 m=4 的情况。
看第一层。
比如说是A,那么第二层一定不能是A,只可能是BCD。
很显然BCD每个下面都会有一个A的可能。
再向下一层,则每个不是A的下面的 m1 种情况都会有一个A,每个A的下面的m-1种情况都不会是A。
然后推到了第n盏灯。
如果第 n1 盏灯的颜色是A的话,那么他下面第 n 盏灯的m1种情况为BCD均满足条件。
如果第 n1 盏灯的颜色不是A,那么要排除A和他本身,也就是说只剩 m2 种情况。
总的来说,是第 n1 盏灯的颜色情况个数乘 m2 加上A的情况个数。
推完了最后一层所有情况的个数即为最终解。
写个图:【0base】排版错乱将就看

A
B C D
A C D | A B D | A B C
BCD ABD ABC| BCD ACD ABC |BCD ACD ABD
……
…… A B C D ……
…… BCD CD BD BC ……


对于前 n1 层的每一层要做的无非是总数目和本层A的数目,而其中A的数目就是上一层不是A的数目。
递推:

an+1=(m1)nan,a0=1.

对于本分支答案,
ans=mn2(m2)+an1.

最后乘个 m 即可。
推着还好吧,然而这样的话相比较答案来说需要的高精度就有加减乘三种了,代码复杂度增加不少,我这种蒟蒻就高精度懵逼了。
其实本质都差不多,这个递推式还蛮简单的只涉及到上一层的情况。
欢迎进行优化。
貌似可以求通项?】【懒了】
上代码:

#include <cstdio>
#include <iostream>
#include <cmath>
#include <vector>
void jin(vector<int> &s){                               //统一进位
    int j=0;
    while(j<s.size()-1){
        s[j+1]+=s[j]/10;
        s[j]%=10;
        j++;
    }
    while(s[j]>=10){
        s.push_back(s[j]/10);
        s[j]%=10;
        j++;                    
    }
}
void print(vector<int> s){                              //打印
    for(vector<int>::reverse_iterator it=s.rbegin();it<s.rend();it++)
        printf("%d", *it);
    printf("\n");
}
vector<int> highMulti(vector<int> s, int m){            //高精乘
    int w=s.size();
    int i=0;
    for(i=0;i<w;i++)
        s[i]*=m;
    jin(s);
    return s;
}
vector<int> highMinus(vector<int> a, vector<int> b){    //高精减
    for(int i=0;i<b.size();i++){
        if(a[i]<b[i]){
            a[i+1]-=1;
            a[i]+=10;
        }
        a[i]-=b[i];
    }
    return a;
}
vector<int> highPlus(vector<int> a, vector<int> b){    //高精加
    int i=0;
    for(i=0;i<b.size();i++)
        a[i]+=b[i];
    jin(a);
    return a;
}
int main(){
    int n, m, k, w;
    scanf("%d%d", &n, &m);
    if(n==1&&m==1){                         //邪恶的特判
        printf("%d\n", 1);
        return 0;
    }
    k=n-1, w=m;                             //k就是换了个下标不用管
    vector<int> wa;                         //然后把m高精度存进这个wa去
    while(w){
        wa.push_back(w%10);                 //倒着存
        w/=10;
    }
    vector<int> a;                          //数列a
    a.push_back(1);
    vector<int> mi;                         //m的幂次
    mi.push_back(1);
    for(int i=0;i<k-1;i++){
        a=highMinus(mi, a);                 //a[i]=m^(i-1)-a[i-1]
        mi=highMulti(mi, m-1);              //mi[i]=mi[i-1]*(m-1)
    }
    vector<int> res=highMulti(highPlus(highMulti(mi, m-2), a), m);
    //res=(mi*(m-2)+a)*m;
    print(res);
    return 0;
}

总的来说还好吧,嗯大概就是这样。


标算

  • f(i)为用 m 种颜色给i盏彩灯着色的方案数, (i=1,2,...n) 。 我们设三盏连续的彩灯依次编号为 i1 i i+1。现在分析彩灯 i1 与彩灯 i+1
    1. 若彩灯 i1 与彩灯 i+1 颜色不同,则去掉彩灯 i ,则变成用m种颜色给 n1 盏彩灯着色的方案数。即 f(i1) 。 因为彩灯 i m2种情况(它与彩灯 i1 i+1 颜色不同),所以此时方案数为 (m2)f(i1)
    2. 若彩灯 i1 与彩灯 i+1 颜色相同,则去掉彩灯 i ,并把彩灯i1 i+1 合并成彩灯 i ,则变成用 m 种颜色给i2盏彩灯着色的方案数。即 f(i2) 。 因为彩灯 i m1种情况(它与彩灯 i1 i+1 颜色不同),所以此时方案数为 (m1)f(i2)
      • 综合1、2得出计算 f(n) 的递归方程式:
        f(n)=(m2)f(n1)+(m1)f(n2).
    • 边界条件: 由上述分析过程可以看出满足递归式的 n 必须大于3。故必须先求出 f(1) f(2) f(3) 的值。很明显:
       
      f(1)=m,f(2)=m(m1),f(3)=m(m1)(m2).
    • 此外,因本题方案数增长很快,因此高精度计算也是必不可少的。
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值