题目链接:http://oj.ecustacm.cn/problem.php?id=1256
分析:
dp+滚动数组:
(代码无法通过全部数据)
dp[i][j]表示高度为 i , 顶面点数为 j 的方案数
dp[ i ][ j ] 就等于 i-1 高度时所有与j的反面无冲突的方案数累加
总方案数还要乘以(4^n), 因为每一个骰子可以4面转
每一层的规划只与前一层有关, 所以可以采用滚动数组
代码:
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int n,m;
int book[7][7];/*book[i][j]=0:点数为i的面与点数为j的面存在冲突*/
void init()
{
for(int i=1; i<=6; i++)
for(int j=1; j<=6; j++)
book[i][j]=1;
}
LL quick_mi(LL x,int n)
{
LL tot=1;
while(n)
{
if(n&1)
tot=tot*x%mod;
x=x*x%mod;
n>>=1;
}
return tot;
}
int main()
{
cin>>n>>m;
init();
int s1,s2;
while(m--)
{
cin>>s1>>s2;/*s1和s2存在冲突*/
book[s1][s2]=0;
book[s2][s1]=0;
}
LL dp[2][7];
int e=0;/*滚动标志*/
for(int i=1; i<=6; i++)
dp[e][i]=1;/*起初,每个面朝上的摆法为1*/
for(int i=2; i<=n; i++)
{
e=1-e;
for(int j=1; j<=6; j++)
{
dp[e][j]=0;/*下面的dp[e][j]+=dp[1-e][k],体现滚动数组的滚动性,这里重新清零*/
for(int k=1; k<=6; k++)
{
if(book[j][k])
dp[e][j]+=dp[1-e][k];/*一层的一种情况加上它低一层的所有情况*/
}
dp[e][j]%=mod;
}
}
LL sum=0;
for(int i=1; i<=6; i++)
sum=(sum+dp[e][i])%mod;
LL ans=quick_mi(4,n);
sum=sum*ans%mod;
printf("%lld\n",sum);
return 0;
}
矩阵快速幂优化:
思想:
引入矩阵求斐波那契数列:
通过对矩阵乘法的巧妙应用, 我们可以写成如此精致的递归式 .
仔细分析, 其实中间的矩阵的列向量是十分特别的, 它以近乎完美的方式对左边矩阵中的点值进行了有机性的取舍, 从而演变出了右边的矩阵.
右边的矩阵与左边的矩阵又具有相似的结构, 所以, 递归式可以无限地演变下去.
最终变为:
改变dp数组的意义:
dp[ i ][ j ]=0表示: 有一个点数为i的面, 它的反面与点数为j的面存在冲突.
例如: 如果1和2存在冲突, 那么dp[4][2] 和dp[5][1] 都为0 , 因为4的反面是1, 5的反面是2.
单行矩阵book[1][ j ]:记录高度为N时, 顶面为j的总方案数.
矩阵book记录了当前某高度下的各个面的总方案数, 而矩阵dp的列向量描述了"选择", 这已经跟斐波那契例子中的结构相当类似了, 从而我们可以证明:
book * dp = newbook
如果book记录了高度为N的各个面的总方案数;
那么 newbook 记录了高度为N+1的各个面的总方案数
当高度为N时, 式子是这样的 :
book = book * dp * dp *…*dp ; ( dp被乘了N-1次)
代码:
#include
#include
#include
using namespace std;
typedef long long LL;
const LL mod=1e9+7;
int n,m;
int to[7]= {0,4,5,6,1,2,3};
struct node
{
LL v[7][7];
} first;
void init()
{
for(int i=1; i<=6; i++)
for(int j=1; j<=6; j++)
first.v[i][j]=1;
}
node mul(node x,node y)/*两个6*6的矩阵*/
{
node t;
for(int i=1; i<=6; i++)
{
for(int j=1; j<=6; j++)
{
t.v[i][j]=0;
for(int k=1; k<=6; k++)
{
t.v[i][j]+=(x.v[i][k]*y.v[k][j]%mod);
t.v[i][j]%=mod;
}
}
}
return t;
}
node mul1(node x,node y)/* 1*6和6*6的矩阵 */
{
node t;
for(int i=1; i<=1; i++)
{
for(int j=1; j<=6; j++)
{
t.v[i][j]=0;
for(int k=1; k<=6; k++)
{
t.v[i][j]+=(x.v[i][k]*y.v[k][j]%mod);
t.v[i][j]%=mod;
}
}
}
return t;
}
node quick_mi(node first,int k)
{
node temp;
for(int i=1; i<=6; i++)/*初始化为单位矩阵*/
for(int j=1; j<=6; j++)
{
if(i==j)
temp.v[i][j]=1;
else
temp.v[i][j]=0;
}
while(k)
{
if(k&1)
temp=mul(temp,first);
first=mul(first,first);
k>>=1;
}
return temp;
}
LL quick_mi1(LL x,int n)
{
LL tot=1;
while(n)
{
if(n&1)
tot=tot*x%mod;
x=x*x%mod;
n>>=1;
}
return tot;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
first.v[to[a]][b]=0;
first.v[to[b]][a]=0;
}
node book;
for(int i=1; i<=6; i++)
book.v[1][i]=1;/*1乘6基础矩阵的book表示以i为顶面的情况数*/
node ans=quick_mi(first,n-1);/*m对冲突情况考虑后的6*6矩阵*/
ans=mul1(book,ans);
LL sum=0;
for(int i=1; i<=6; i++)/*此处还是dp累加的思想*/
sum=(sum+ans.v[1][i])%mod;
LL xx=quick_mi1(4,n);
sum=sum*xx%mod;
printf("%lld\n",sum);
}
return 0;
}
不要温顺的走进那个良夜,激情不能被消沉的暮色淹