• HDU 5729


    题意:
        对于一个由n*m个1*1的菱形组成可任意扭曲的矩形(姑且这么说),求添加斜线*(两种)让菱形变成正方形,使得整个矩形固定且无法扭曲的方案数。

    分析:
        n*m的矩形有如下性质:( 平行具有传递性 )
            任意一行的每一条竖边永远保持平行,任意一列的每一条横边永远保持平行
        
        当一个单位格加上斜边的时候,这个单位格的形状就不能改变,实质上就是组成这个单位格的横边和竖边保持着一个垂直关系.


        一旦组成这个小单元格的横边和竖边保持了一个垂直关系,那么横边所在的列和竖边所在的行均能保持垂直关系.


        即任意一行和任意一列均为一个整体


        那么题意即为使任意列的横边和任意行竖边均保持垂直关系


        还有二维平面上两列横边若垂直同一行竖边,则两列横边互相平行,即它们可以看做一个整体(属于同一个集合)


        于是,将纵边抽象为点集(size为行数),将横边抽象为点集(size为列数),垂直关系抽象为连线,即求连通二分图的方案数。

    转移方程式:
        DP[n][m] = 3^(n*m) - ∑( DP[i][j] * C[i-1][n-1] * C[j][m] * 3^( (n-i)*(m-j) ) ) (i!=n||j!=m);
        意为,所有状态数 -  两点集中选取(i,j)个点在同一个连通块内,剩下的点任意状态

    注意点:
        1.必须枚举固定的一个点所在的连通块情况以避免计数重复, 那么这个连通块有一个点是确定的了,所以,共 i 条横边的枚举为 C[i-1][n-1],且i,j是等价的,故只选择其中任意一边就可以了
          
        2.另外,因为选择的是 所有情况 - 不合理情况 的计数方法,不合理情况至少有两个以上的连通块连通块内,故 i==n且j==m 的情况为合法情况,此时该退出循环

    了解来龙去脉之后,代码就显得不那么重要了。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 using namespace std;
     5 const int MOD = 1000000007;
     6 long long C[105][105],pow3[105];//组合数,幂 
     7 long long dp[11][11];
     8 void Init()
     9 {
    10     C[0][0]=1; 
    11     for(int i = 1; i <= 100; i++){//组合数 C[下标][上标] 
    12         C[i][0] = C[i][i] = 1;
    13         for(int j = 1; j < i; j++)
    14             C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD;
    15     }
    16     pow3[0] = 1;
    17     for(int i = 1; i <= 100; i++) //3的幂 
    18         pow3[i] = 3 * pow3[i-1] % MOD;
    19 }
    20 void CalDP()
    21 {
    22     dp[1][0] = dp[0][1] = 1; //重要的边界条件,一个点时必然联通且方案唯一 
    23     long long tmp;
    24     for(int i = 1; i <= 10; i++)
    25     {
    26         for(int j = 1; j <= 10; j++)
    27         {
    28         //    if(i==1||j==1) continue;
    29             dp[i][j] = pow3[i * j];//总数目
    30             for(int ii = 1; ii <= i; ii++)//至少有自己 
    31             {
    32                 for(int jj = 0; jj <= j; jj++)
    33                 {
    34                     if(ii == i && jj == j) continue; 
    35                     tmp = C[i-1][ii-1] * C[j][jj] % MOD;
    36                     tmp = tmp * dp[ii][jj] % MOD;
    37                     tmp = tmp * pow3[ (i-ii)*(j-jj) ]%MOD;
    38                     dp[i][j] = (dp[i][j] - tmp + MOD) % MOD;
    39                 }
    40             }
    41         }
    42     }
    43 }
    44 int main()
    45 {
    46     Init();
    47     CalDP();
    48     int n,m;
    49     while(~scanf("%d%d",&n,&m))
    50     {
    51         printf("%lld
    ",dp[n][m]);
    52     }
    53 }
    我自倾杯,君且随意
  • 相关阅读:
    实体枚举字段注释反向生成数据库注释sql
    系统间数据存储和交互思路
    复选框与bitmap算法实践
    Entity Framework Core配置DbContext的两种方式
    C#语法糖——持续更新
    抽丝剥茧读源码——Microsoft.Extensions.Configuration(2)
    抽丝剥茧读源码——Microsoft.Extensions.Configuration(1)
    算法分享之关于atcoderbeginner166E的讲解
    关于coder168E问题的分析与解答(C语言)
    atcoder168D题
  • 原文地址:https://www.cnblogs.com/nicetomeetu/p/5705542.html
Copyright © 2020-2023  润新知