zoukankan      html  css  js  c++  java
  • 01 矩阵

    题目背景:

    幻想乡的魔法使,雾雨魔理沙,发明了一种有趣的魔法卡片。
    这种魔法卡片为正方形,分成若干个大小相同的方格,每个方格上都有一种印记,印记
    分为两种:”绮罗印记” 和 “幽光印记”。卡片上每一行,每一列的方格中都有两个 “绮罗印记”,其他方格为 “幽光印记”。
    这种卡片的神奇之处就在于,你可以任意次数地交换任意两行的所有方格,或者交换任
    意两列的所有方格。如果两张卡片可以通过这种行列交换变成相同的卡片,我们就认为它们是相同的。
    琪露诺(9),希望知道在一定尺寸下,共有多少种不同的魔法卡片,你能告诉他吗?

    问题描述:

    给定正整数 n ,对于一个 n*n 的 01 矩阵,如果每行元素之和都为 2,且每列元素之和
    都为 2,我们就称它是“可调整的”。
    如果两个“可调整的”矩阵可以通过若干次交换矩阵的两行或两列变成相同的,我们认
    为它们是等价的“可调整的”矩阵。
    求不同的“可调整的”矩阵的个数,输出答案对 998244353 取模的结果。
    注意有多组数据。

    输入格式:

    第一行一个正整数 T,表示数据组数。
    接下来 T 行,每行一个正整数 n,表示矩阵的大小。
    输出格式:
    应包含 T 行,每行一个数,表示你的答案。

    样例数据 1:
    card.in
    1
    2
    card.out
    1

    样例数据 2:
    card.in
    2
    3
    4
    card.out
    1
    2

    样例解释:
    当 n = 3 时有 1 种方案:
    × ×
    ××
    ××
    当 n = 4 时有 2 种方案:
    (1)
    × ×
    ××
    × ×
    × ×
    (2)
    × ×
    ××
    ××
    × ×

    数据范围:

    对于前 20% 的数据,1<= n <= 7
    对于前 40% 的数据,1 <= n <= 10
    对于前 70% 的数据,1 <= n <= 100
    对于 100% 的数据,1 <= n <= 1000,T <= 10

    【题解】

    算法一:
    暴力,搜索。 期望得分:
    20 ~ 40 分

    算法二:
    通过观察,我们可以发现,这个问题本质上是求将 n 划分成大于等于 2 的数的方案数。
    考虑 DP , f[i][j][k] 表示将 i 划分成 j 个数,且最大的数为 k 的方案数。
    有转移方程: f[i][j][k] = sum{f[i - k][j - 1][x]} (x <= k) 边界条件: f[2][1][2] = 1
    优化: f[i + 1][j][k + 1] = sum{f[i - k][j - 1][x]} (x <= k + 1)= f[i][j][k] + f[i - k][j - 1][k + 1]
    时间复杂度: O(n^3~n^4)
    期望得分: 40 ~ 70 分

    算法三:
    在算法二的基础上改进 DP 方程。 记f(i, j) 为将 j 分成 i 份时的方案数。
    f(1,2) = 1,f(i, j) = f(i − 1, j − 2) + sgn(j > i)*f(i, j − i) 分两种情况讨论:
    1.划分出的最小数为 2,方案数为 f(i − 1,j − 2),即在将 j − 2 分成 i − 1 份的划分方案的基础上再多划分一个数 2 的方案数。
    2. 划分出的最小数大于 2,方案数为 f(i,j − i),即将 j − i 分成 i 份时划分出的 i 个数都加 上 1 的方案数。
    时间复杂度: O(n^2)
    期望得分: 100 分

    还有更优的算法吗?
    算法四 :
    注意到输入只有 n ,在算法二 / 算法三的基础上打表。
    时间复杂度: O(1) 期望得分: 100 分

    然后我就不能理解为什么这个问题本质上是求将 n 划分成大于等于 2 的数的方案数。。。

    根据某个大佬的讲解,把结果图形的各个点连起来后:
    这里写图片描述
    然后找规律得出结论。
    但是,似乎还可用网络流二分图思想解释,有时间再说吧。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<vector>
    #include<algorithm>
    using namespace std;
    #define MOD 998244353
    inline int read()
    {
        register int x=0,t=1;
        register char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-'){t=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
        return x*t;
    }
    int f[1010][1010];
    int main()
    {
        int T=read();
        f[0][0]=1;
        //f[i][j]表示把i划分成j个数的方案
        for(int i=2;i<=1000;++i)
            for(int j=1;j<=i/2;++j)//最多只能够分成i/2个数
            {
                (f[i][j]+=f[i-2][j-1])%=MOD;//第一种情况,i由i-2多划分一个j
                (f[i][j]+=f[i-j][j])%=MOD;//第二种情况,将i-j分出的j个数都加上1
            }
        while(T--)
        {
            int n=read(),ans=0;
            for(int i=n;i;i--)(ans+=f[n][i])%=MOD;
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    1600802101
    Android第二次作业
    android 第一次作业
    团队作业—项目答辩
    软件工程—团队作业2.2
    软件工程—团队作业2
    软件工程—团队作业1
    第一篇博客
    Android第四次作业
    作业3
  • 原文地址:https://www.cnblogs.com/yanshannan/p/7413146.html
Copyright © 2011-2022 走看看