zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    第一次作业
    java基础随笔09
    java基础随笔08
    java基础随笔07
    java基础随笔06
    java基础随笔05
    java基础随笔04
    java基础随笔03
    java基础随笔02
    java基础随笔01
  • 原文地址:https://www.cnblogs.com/wdt1/p/13620722.html
Copyright © 2011-2022 走看看