b_lq_骰子的点数 & 叠骰子(暴力枚举点数 / 滚动数组优化内存 | 滚动数组)
骰子的点数(aw)
将一个骰子投掷n次,获得的总点数为s,s的可能范围为n~6n。
掷出某一点数,可能有多种掷法,例如投掷2次,掷出3点,共有[1,2],[2,1]两种掷法。
请你求出投掷n次后,得到n~6n点分别有多少种掷法。
样例1
输入:n=1
输出:[1, 1, 1, 1, 1, 1]
解释:投掷1次,可能出现的点数为1-6,共计6种。每种点数都只有1种掷法。所以输出[1, 1, 1, 1, 1, 1]。
样例2
输入:n=2
输出:[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]
解释:投掷2次,可能出现的点数为2-12,共计11种。每种点数可能掷法数目分别为1,2,3,4,5,6,5,4,3,2,1。
所以输出[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]
方法一:dp
- 定义状态:
- f[i][j] 表示第 i 次扔,且扔出点数总和为 j 的方案数
- 思考初始化:
- f[1][1...6]=1
- 思考状态转移方程:
- f[i][j] += f[i-1][j-k],j∈[1,2i],k∈[j,6],即本次扔骰子的的方案数等于累加上前一次扔的方案数
- 思考输出:...
const int N=1e3;
class Solution {
public:
int f[N][N];
vector<int> numberOfDice(int n) {
for (int i=1; i<=6; i++) f[1][i]=1;
for (int i=2; i<=n; i++)
for (int j=1; j<=6*i; j++)
for (int k=1; k<=min(j,6); k++){
f[i][j]+=f[i-1][j-k];
}
vector<int> ans;
for (int i=n; i<=6*n; i++) ans.emplace_back(f[n][i]);
return ans;
}
};
复杂度分析
- Time:\(O(n^2)\),
- Space:\(O(n^2)\),
方法二:滚动数组
由于每个骰子只扔一次,故和01背包一样,把总点数看成背包容量,则总点数的枚举需要从后往前
const int N=1e3;
class Solution {
public:
int f[N];
vector<int> numberOfDice(int n) {
for (int i=1; i<=6; i++) f[i]=1;
for (int i=2; i<=n; i++)
for (int j=6*i; j>=1; j--) {
f[j]=0;
for (int k=min(j,6); k>=1; k--) //上一次扔骰子的点数
f[j]+=f[j-k];
}
vector<int> ans;
for (int i=n; i<=6*n; i++) ans.emplace_back(f[i]);
return ans;
}
};
复杂度分析
- Time:\(O(n^2)\),
- Space:\(O(n)\),
叠骰子(lq)
我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。
假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。
atm想计算一下有多少种不同的可能的垒骰子方式。
两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
思路
f[i][j]表示扔完第i个骰子后,第i个骰子的上面是数字j的方案数;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7, N=10;
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
ll n,m,f[2][N],mp[N],cf[N][N];
cin>>n>>m, memset(f, 0, sizeof f), memset(cf, false, sizeof cf), memset(mp, 0, sizeof cf);
for (int i=0; i<m; i++) {
int a,b; cin>>a>>b;
cf[a][b]=cf[b][a]=1;
}
mp[1]=4,mp[4]=1, mp[2]=5,mp[5]=2, mp[3]=6,mp[6]=3;
for (int j=1; j<=6; j++) f[0][j]=4;
ll st=1, c=1, ans=0;;
for (int i=1; i<n; i++) {
for (int j=1; j<=6; j++) { //j:枚举扔完第i个骰子后的筛子上面的数字;k:枚举第i-1个骰子的上面的数字
f[st][j]=0;
for (int k=1; k<=6; k++) if (!cf[mp[j]][k])
f[st][j]=(f[st][j]+f[st^1][k])%mod;
}
st^=1, c=(c*4)%mod; //每次扔完一个都可以对水平的四个面进行旋转
}
for (int j=1; j<=6; j++) ans=(ans+f[st^1][j])%mod;
cout<<ans*c%mod;
return 0;
}