0
点赞
收藏
分享

微信扫一扫

垒骰子(动态规划+矩阵快速幂)

49路末班车 2022-02-12 阅读 26

 我看到这道题目的第一想法就是动态规划,设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;
}
举报

相关推荐

0 条评论