问题描述
【题目描述】
【输入】
【输出】
【样例输入】
2 1
1 2
【样例输出】
544
题目解析
暴力法求解
假设最顶层的上面那个面是1,n表示骰子数目,调用f(1,n-1),也就是递归调用f求出剩余n-1骰子垒出的方案数,接着分别假设最顶层的上面那个面是2、3、4、5、6,同样的方法用f求出各自方案对应的剩余n-1骰子的方案数。在求解过程中利用 c o n f l i c t [ ] [ ] conflict[][] conflict[][]二维数组对当前状态加以判断,如果存在冲突就 c o n t i n u e continue continue继续执行下一种方案。注意我们考虑的所有方案数最后都要*4因为前后左右可以来回掉换位置。
用这种方法可以解决30%的问题数,对于大规模的问题求解计算量太大,我们考虑以下两种方法。
动态规划求解
动态规划的关键就是
d
p
[
]
[
]
dp[][]
dp[][]数组的更新迭代。我们令
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示在第
i
i
i层
j
j
j在上面这个面的方案数。我们从最后一层开始递推,其表达式是
d
p
[
i
]
[
j
]
=
∑
x
=
1
6
d
p
[
i
−
1
]
[
x
]
∣
o
p
[
j
]
与
x
不
冲
突
dp[i][j]=\sum_{x=1}^{6}dp[i-1][x]|op[j]与x不冲突
dp[i][j]=x=1∑6dp[i−1][x]∣op[j]与x不冲突当推到最上面一层时,直接用
a
n
s
=
∑
x
=
1
6
d
p
[
n
]
[
x
]
ans=\sum_{x=1}^{6}dp[n][x]
ans=x=1∑6dp[n][x]
矩阵求解
沿用上一种解法的思路,进一步优化,假设
f
(
i
,
j
)
=
∑
k
=
1
6
f
(
i
−
1
,
k
)
∣
o
p
[
j
]
与
k
不
冲
突
f(i,j)=\sum_{k=1}^{6}f(i-1,k)|op[j]与k不冲突
f(i,j)=k=1∑6f(i−1,k)∣op[j]与k不冲突我们怎么才能快速的从
f
1
f_1
f1到
f
n
f_n
fn呢,假设一个列向量
[
f
i
−
1
,
f
i
−
2
,
.
.
.
f
i
−
n
]
T
=
[
1
,
1
,
.
.
.
1
]
T
[f_{i-1},f_{i-2},...f_{i-n}]^T=[1,1,...1]^T
[fi−1,fi−2,...fi−n]T=[1,1,...1]T,以及一个冲突矩阵
T
T
T,以样例输入为例,1面和2面冲突,则冲突矩阵
T
T
T就是
[
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
1
1
1
0
1
1
1
1
1
1
1
1
1
]
\begin{bmatrix} &1 &1 &1 &1 &1 \\ &1 &1 &1 &1 &1 \\ &1 &1 &1 &1 &1 \\ &1 &0 &1 &1 &1 \\ &0 &1 &1 &1 &1 \\ &1 &1 &1 &1 &1 \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎡111101111011111111111111111111⎦⎥⎥⎥⎥⎥⎥⎤
表示当前上面这个面为4时,则下一层的上面这个面不能为2,因为4的底面是1,1与2冲突,同理当前上面这个面为5时,下一层上面这个面不能为1。
存在这样关系 f _ n = T n − 1 × [ 1 , 1 , . . . 1 ] T f_\_n=T^{n-1}×[1,1,...1]^T f_n=Tn−1×[1,1,...1]T求出的 f _ n f_\_n f_n是一个列向量,把这个列向量各个数相加就是 a n s ans ans。
C++代码
暴力法代码
#include<bits/sdtc++.h>
using namespace std;
#define MOD 1000000007
int op[7]; //面对面的骰子数
bool conflict[7][7];
int n,m;
long long int f(int up,int cnt) //递归
{
if(cnt==0) return 4; //前面n个骰子都定好了这就是4个方案
long long ans = 0;
for(int upp=1;upp<=6;upp++)
{
if(conflict[op[up]][upp]) continue;
ans+=f(upp,cnt-1);
}
return ans;
}
void init()
{
op[1]=4;
op[4]=1;
op[2]=5;
op[5]=2;
op[3]=6;
op[6]=3;
}
int main()
{
init();
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int x,y;
scanf("%d %d",&x,&y);
conflict[x][y] = true;
conflict[y][x] = true;
}
long long ans = 0;
for(int up=1;up<=6;up++) //最上面的面的数字是up
{
ans = (ans+4*f(up,n-1))%MOD;
}
printf("%lld",ans);
return 0;
}
dp法代码
#include<bits/stdc++.h>
using namespace std;
#define MOD 1000000007
long long dp[2][7]; //dp[i][j]表示有i层,定朝上的数字为j的稳定方案数
int n,m;
bool conflict[7][7];
map<int,int> op;
void init()
{
op[1] = 4;
op[4] = 1;
op[2] = 5;
op[5] = 2;
op[3] = 6;
op[6] = 3;
}
int main()
{
init();
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d %d",&a,&b);
conflict[a][b] = true;
conflict[b][a] = true;
}
for(int j=1;j<=6;j++)
dp[0][j] = 1;
int cur = 0; //连续滚动
for(int level=2;level<=n;level++) //迭代层数
{
cur = 1-cur;
for(int j=1;j<=6;j++) //尝试将6个面放在当前一层朝上的方向
{
dp[cur][j] = 0;
for(int i=1;i<=6;i++) //将与op[j]不冲突的上一层个字里面的数累加起来
{
if(conflict[op[j]][i]) continue;
dp[cur][j] = (dp[cur][j]+dp[1-cur][i])%MOD;
}
}
}
long long sum = 0;
for(int k=1;k<=6;k++)
sum = (sum+dp[cur][k])%MOD;
long long ans = 1; //快速幂求4的n次方
long long tmp = 4;
long long p = n;
while(p!=0)
{
if(p&1==1) ans = (ans*tmp)%MOD;
tmp = (tmp*tmp)%MOD;
p>>=1;
}
printf("%lld",(sum*ans)%MOD);
return 0;
}
矩阵法代码
#include<bits/stdc++.h>
using namespace std;
#define MOD 1000000007
typedef long long LL;
int n,m;
map<int,int> op;
void init()
{
op[1] = 4;
op[4] = 1;
op[2] = 5;
op[5] = 2;
op[3] = 6;
op[6] = 3;
}
struct M
{
LL a[6][6];
M()
{
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
a[i][j] = 1;
}
};
M mMultiply(M m1,M m2)
{
M ans;
for(int i=0;i<6;i++)
{
for(int j=0;j<6;j++)
{
ans.a[i][j] = 0;
for(int k=0;k<6;k++)
ans.a[i][j]=(ans.a[i][j]+m1.a[i][k]*m2.a[k][j])%MOD;
}
}
return ans;
}
M mPow(M m,int k) //求M的k次方
{
M ans; //单位矩阵
for(int i=0;i<6;i++)
{
for(int j=0;j<6;j++)
{
if(i==j) ans.a[i][j] = 1;
else ans.a[i][j] = 0;
}
}
while(k!=0)
{
if(k&1==1)
{
ans = mMultiply(ans,m);
}
m = mMultiply(m,m);
k>>=1; //向右移动1位
}
return ans;
}
int main()
{
init();
scanf("%d %d",&n,&m);
M cMatrix;
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d %d",&a,&b);
//完善冲突矩阵
cMatrix.a[op[a]-1][b-1] = 0;
cMatrix.a[op[b]-1][a-1] = 0;
}
M cMatrix_n_1 = mPow(cMatrix,n-1); //冲突矩阵的n-1次方
LL ans = 0;
for(int j=0;j<6;j++)
for(int i=0;i<6;i++)
ans = (ans+cMatrix_n_1.a[i][j])%MOD;
LL t = 1;
LL tmp = 4;
LL p = n;
while(p!=0)
{
if(p&1==1) t = (t*tmp)%MOD;
tmp = (tmp*tmp)%MOD;
p>>=1;
}
printf("%lld",ans*t%MOD);
return 0;
}