我看到这道题目的第一想法就是动态规划,设dp[i][j]表示我们枚举了i个骰子且最上面为j的方案数,那么很显然初始化为dp[1][j]=4(j=1~6)(侧面方向不同按照不同方案来计算),递推过程很简单,就是dp[i][j]=vis[(j+2)%6+1][k]*dp[i-1][k](k=1,2,3,4,5,6),(j+2)%6+1代表是j的对立面的数字,分析我以为这道题目就算是完成了,然后写完代码一运行结果也对,提交后才发现超时,这个时候我一看数据范围是1E9,才知道线性DP是不可能做出来的,一般需要用到矩阵快速幂来进行优化,有了dp[i][j]=vis[(j+2)%6+1][k]*dp[i-1][k]这个递推式,我们就可以得到递推矩阵
vis[i][j]=0或者4,若为0说明i和j不能相邻,否则就对应四种方案(侧面顺序任意),答案矩阵初值f(1,j)=4(j=1,2,3,4,5,6) ,按照这个过程进行矩阵快速幂求解即可
下面是代码:
未用快速幂递推的代码(超时代码):
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N=1003,mod=1e9+7;
ll dp[N][7];//dp[i][j]表示由i个骰子组成顶部为j的方案数
int vis[7][7];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=6;i++)
for(int j=1;j<=6;j++)
vis[i][j]=4;
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
vis[u][v]=vis[v][u]=0;
}
for(int i=1;i<=6;i++)
dp[1][i]=4;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=6;j++)
for(int k=1;k<=6;k++)
dp[i][(j+2)%6+1]=(dp[i][(j+2)%6+1]+dp[i-1][k]*vis[j][k])%mod;
}
ll ans=0;
for(int i=1;i<=6;i++)
ans+=dp[n][i];
printf("%lld",ans);
return 0;
}
下面是用矩阵快速幂递推的代码(AC代码):
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int vis[7][7];
void mul(ll ans[][7],ll a[][7],ll b[][7])
{
ll t[7][7]={0};
for(int i=1;i<=6;i++)
for(int j=1;j<=6;j++)
for(int k=1;k<=6;k++)
t[i][j]=(t[i][j]+a[i][k]*b[k][j])%mod;
memcpy(ans,t,sizeof t);
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=6;i++)
for(int j=1;j<=6;j++)
vis[i][j]=4;
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
vis[u][v]=vis[v][u]=0;
}
ll t[7][7]={
{0,0,0,0,0,0,0},
{0,vis[1][4],vis[1][5],vis[1][6],vis[1][1],vis[1][2],vis[1][3]},
{0,vis[2][4],vis[2][5],vis[2][6],vis[2][1],vis[2][2],vis[2][3]},
{0,vis[3][4],vis[3][5],vis[3][6],vis[3][1],vis[3][2],vis[3][3]},
{0,vis[4][4],vis[4][5],vis[4][6],vis[4][1],vis[4][2],vis[4][3]},
{0,vis[5][4],vis[5][5],vis[5][6],vis[5][1],vis[5][2],vis[5][3]},
{0,vis[6][4],vis[6][5],vis[6][6],vis[6][1],vis[6][2],vis[6][3]},
};
ll ans[7][7]={
{0,0,0,0,0,0,0},
{0,1,0,0,0,0,0},
{0,0,1,0,0,0,0},
{0,0,0,1,0,0,0},
{0,0,0,0,1,0,0},
{0,0,0,0,0,1,0},
{0,0,0,0,0,0,1},
};
n--;
while(n)
{
if(n&1) mul(ans,ans,t);
n>>=1;
mul(t,t,t);
}
ll sum=0;
for(int i=1;i<=6;i++)
for(int j=1;j<=6;j++)
sum=(sum+ans[i][j]*4)%mod;
printf("%lld",sum);
return 0;
}