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;
}
posted @ 2020-09-06 09:33  童年の波鞋  阅读(285)  评论(0编辑  收藏  举报