一、概述
1. 问题描述
有n个格子3种颜色,现在向每个格子着色,条件是:
1)每个格子与相邻格子颜色不同
2)首格与尾格颜色不同
问共有多少种着色方法?
2. 问题链接
HDU OJ -- LELE的RPG难题(ACM Steps: 3.1.6)
3. 问题截图
图1.1 问题截图
二、算法思路
现通过取不同的n值分析并说明算法的思路。
约定如下:
1)下述图中节点表示颜色,其中
V节点表示:第1个格子所用的颜色
X节点表示:第1个格子没有用到的其余两个颜色中的一个
2)图中节点旁边的权值表示当前节点可能的情况种数,即为格子着节点对应颜色的方案数
3)图中的p(i)表示当前为第i个格子着色
4)f(m)表示当n = m时,可行解的个数
1. n = 1时
如图2.1所示,当只有一个格子时,它可以着3色,且因为它是第一个格子,故用V节点表示,权值3表示有3种不同的着色方案。
记,f(1) = 3
图2.1 n = 1时满足条件的情况
2. n = 2时
如图2.2所示,p(1),即为第1个格子着色,有3中选择。
p(2),由于要满足条件1,2(如上面的问题描述所述),此时第2个格子只能选择X节点,并且权值为2,表示可选的颜色有2种。
记,f(2) = 3 x 2 = 6
图2.2 n = 2时满足条件的情况
3. n = 3时
如图2.3所示,n = 3时所有可能的情况均已列出,p(1)、p(2)如上所述。
由于条件1,p(3)不能与p(2)的颜色相同,故此时有2种选择,即X节点和V节点(此时X节点对应的颜色与p(2)的X节点对应的颜色不同);
由于条件2,V节点不能选择,图中用虚线表示,表示V节点被剪除,故此时只能选择X节点。
记,f(3) = 3 x 2 x 1 = 6
图2.3 n = 3时满足条件的情况
4. n = 4时
如图2.4所示,p(1)、p(2)如上所述。
由于此时p(3)不是最后一个节点,故有两种选择。
1)当p(3)的X节点向p(4)扩展时,由于条件1、2,此时p(4)只能扩展为X节点,对应的V节点被剪除。
现分析图中用红色圈出的部分,它其实就是n = 3时的情况。n = 3时满足条件的叶子节点必定为X节点,当n = 4时由此X节点向下扩展由于要满足条件1、2,此时也只能扩展为X节点,由n = 3的结果多加了一层唯一的X节点,最后的结果不变,故此部分对应的可行解数目仍然为f(3)。
2)当p(3)的V节点向p(4)扩展时,满足条件1即满足条件2,因为p(4)的相邻节点是V节点,即此节点与第一个格子着色相同,故此时扩展为X节点,且权值为2,表示有两种颜色可以选择。
这个扩展(用蓝色方框框出),其实就是n = 3时被剪除的部分,这部分涉及的可行解的数目为:(3 x 2) x 2 = 12
综上,记f(4) = f(3) + 12 = 18
图2.4 n = 4时满足条件的情况
5. n = 5时
如图2.5所示,如上所述,红色圈出部分对应n = 4时的可行解,蓝色方框表示的是n = 4时剪除的V节点,由于被剪除1个,所以蓝色方框对应部分可以表示为(3 x 2) x (1 x 2),(3 x 2)表示p(1)、p(2)对应的情况数,(1 x 2)表示n = 4时剪除了1个节点,且这个节点必定会派生出两个可行的X节点作为叶子节点。
记,f(5) = f(4) +(3 x 2) x (1 x 2)= 30
图2.5 n = 5时满足条件的情况
综上所述,当n > 3时,问题的求解一方面依赖于前面小规模问题解答的数目,一方面依赖于前面所剪除的V节点数,故问题的关键在于表达出n取不同值时前面所剪除的V节点数。
可以从图中看出:
n = 3时,最后一层具有2个节点,其中1个为有效节点,另一个为被剪除节点,记为(a[3],b[3],c[3]=a[3]-b[3])=(2,1,2-1)
n = 4时,最后一层具有4个节点,记为a[4],
有效节点的个数b[4] = (n = 3时的有效节点 )+ (n = 3时被剪除的V节点数)x 2 = b[3] + c[3] x 2,因为上一层每剪除一个V节点,在下一层都对应了两个有效的叶子节点,
剩余的节点数c[4] = a[4] - b[4]
n = m时,(a[m],b[m],c[m]) = ((m - 2)x 2,b[m-1] + c[m-1] x 2,am - bm)
有了被剪除V节点数c[m],f(n)=f(n-1) + c[n-1] x 2 x(3 x 2)
三、算法实现
#include <iostream>
using namespace std;
const int MAXSIZE = 51;
unsigned long long res[MAXSIZE];
int main()
{
long long twos=2, cur=1, rem=1;
res[1] = 3;
res[2] = 6;
res[3] = 6;
for(int i=4; i<MAXSIZE; i++){
res[i] = res[i-1] + (6 * 2 * rem);
twos *= 2;
cur += (rem*2);
rem = twos-cur;
}
int n;
while(cin >> n){
cout << res[n] << endl;
}
return 0;
}