zoukankan      html  css  js  c++  java
  • 【noip模拟】局部最小值

    TimeLimit: 1000ms               MemoryLimit: 256MB

    Description

    有一个n行m列的整数矩阵,其中1到n×m之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。

    给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

    Input

    输入第一行包含两个整数n和m(1<=n<=4, 1<=m<=7),即行数和列数。以下n行每行m个字符,其中“X”表示局部极小值,“.”表示非局部极小值。

    Output

    输出仅一行,为可能的矩阵总数除以998244353的余数。

    Sample Input

    【样例输入1】
    2 4
    .X..
    ...X
    【样例输入2】
    4 2
    X.
    ..
    ..
    X.
    【样例输入3】
    1 2
    XX

    Sample Output

    【样例输出1】
    2100
    【样例输出2】
    2520
    【样例输出3】
    0

    HINT

    对于10%的数据,$n≤3,m≤3$

    对于20%的数据,$n≤3,m≤4$

    对于45%的数据,$n≤4,m≤4$

    对于60%的数据,$n≤4,m≤5$

    对于80%的数据,$n≤4,m≤6$

    对于100%的数据,$n≤4,m≤7$

    [吐槽]

      这题的话。。在模拟题里面出现了。。两遍啊哈哈。。

      第一次做的时候啃得挺。。辛苦。。不过不得不说这个状压还是很好玩的

    [题解]

      首先看看这个局部最小值有什么可以利用的地方。。

      画一下图就会发现好像最多只有8个

      这个数字很小所以就可以。。。

      考虑状压

      考虑用$f_{i,j}$表示填完了$1$到$i$这$i$个数,局部最小值的填充情况为$j$的方案数

      用$cnt_i$表示局部最小值的填充情况为$i$时,还有多少个位置是可以填数的

      那么就可以得到:

      $f_{i,j} = f_{i-1,j}*(cnt_j - (i -1)) + sumlimits_{kin j} f_{i-j,k}$

      为啥是$cnt_j - (i - 1) $呢

      因为本来有$cnt_j$个位置可以填,但是现在已经有$i - 1$个位置已经确定下来了,并不能填所以要减掉

      考虑cnt怎么算

      首先我们枚举一下状态$ i (0 <= i <= (1<<tot) -1 )$ (tot为X的个数)

      因为现在已经填充的局部最小值有哪些是确定了的,那就意味着状态中非填充的局部最小值是不能填的

      然后因为我们接下来填的数是剩下的数中最小的,所以这个数肯定不能填在局部最小值的相邻位置

      那么就很显然是枚举一下$i$中没有填充的局部最小值,然后把这些地方减掉就好啦

      然后就是$k$怎么枚举

      我们可以每次取lowbit,统计完之后就把最后一位去掉(实现起来就是$k$ & $(k - 1) $)就ok了

      最后还有一个小小的问题

      题目要求只能有这些标为X的地方是局部最小值,其他的地方不能是

      而我们的这种奇妙填数方法可能会导致有别的格子不小心变成了局部最小值了,这是不合法的

      所以就应该要把这些方案减掉

      具体实现其实很简单粗暴,直接枚举有哪些位置可能变成局部最小值

      用同样的方法求出方案数,然后减掉就好(其实就是一个dfs巨无敌粗暴)

      

      然后就很愉快滴搞完啦ovo

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #define MOD 998244353
      5 #define ll long long
      6 using namespace std;
      7 const int dx[9]={0,-1,0,1,0,-1,-1,1,1};
      8 const int dy[9]={0,0,1,0,-1,-1,1,-1,1};
      9 struct xxx
     10 {
     11     int x,y;
     12 }X[110];
     13 char a[10][10];
     14 ll b[10][10],cnt[1<<9],f[110][1<<9];
     15 int n,m,tot;
     16 ll ans;
     17 int dfs(int x,int y,int op);
     18 ll calc();
     19 bool ok(int x,int y);
     20 bool check();
     21 
     22 int main()
     23 {
     24     scanf("%d%d
    ",&n,&m);
     25     for (int i=1;i<=n;++i)
     26     {
     27         for (int j=1;j<=m;++j)
     28             scanf("%c",&a[i][j]);
     29         scanf("
    ");
     30     }
     31     if (!check()) {printf("0
    ");return 0;}
     32     ans=0;
     33     dfs(1,1,0);
     34     printf("%lld
    ",ans);
     35 }
     36 
     37 int dfs(int x,int y,int op)
     38 {
     39     int mark=op?-1:1;
     40     if (x==n+1)
     41     {ans=(ans+MOD+mark*calc())%MOD;return 0;}
     42     if (a[x][y]=='.')
     43     {
     44         a[x][y]='X';
     45         if (check())
     46         {
     47             if (y==m) dfs(x+1,1,op^1);
     48             else dfs(x,y+1,op^1);
     49         }
     50         a[x][y]='.';
     51     }
     52     if (y==m) dfs(x+1,1,op);
     53     else dfs(x,y+1,op);
     54 }
     55 
     56 ll calc()
     57 {
     58     tot=0;
     59     for (int i=1;i<=n;++i)
     60         for (int j=1;j<=m;++j)
     61             if (a[i][j]=='X')
     62                 X[++tot].x=i,X[tot].y=j;
     63     int x,y;
     64     memset(cnt,0,sizeof(cnt));
     65     for (int i=0;i<1<<tot;++i)
     66     {
     67         for (int j=1;j<=n;++j)
     68             for (int k=1;k<=m;++k)
     69                 b[j][k]=1;
     70         for (int j=0;j<tot;++j)
     71         {
     72             if (1<<j&i) continue;
     73             x=X[j+1].x,y=X[j+1].y;
     74             b[x][y]=0;
     75             for (int k=1;k<=8;++k)
     76                 if (ok(x+dx[k],y+dy[k]))
     77                     b[x+dx[k]][y+dy[k]]=0;
     78         }
     79         for (int j=1;j<=n;++j)
     80             for (int k=1;k<=m;++k)
     81                 cnt[i]=(cnt[i]+b[j][k])%MOD;
     82     }
     83     //for (int i=0;i<1<<tot;++i) printf("%d ",cnt[i]);
     84     //printf("
    ");
     85     for (int i=0;i<=n*m;++i)
     86         for (int j=0;j<1<<tot;++j)
     87             f[i][j]=0;
     88     f[0][0]=1;
     89     for (int i=1;i<=n*m;++i)
     90     {
     91         for (int j=0;j<1<<tot;++j)
     92         {
     93             f[i][j]=((ll)f[i-1][j]*((cnt[j]-(i-1))%MOD))%MOD;
     94             for (int k=j;k;k&=(k-1))
     95             {
     96                 x=k&(-k);
     97                 f[i][j]=((ll)f[i][j]+f[i-1][j-x])%MOD;
     98             }
     99         }
    100     }
    101     return f[n*m][(1<<tot)-1];
    102 }
    103 
    104 bool ok(int x,int y)
    105 {
    106     if (x<1||y<1||x>n||y>m) return false;
    107     return true;
    108 }
    109 
    110 bool check()
    111 {
    112     int x,y;
    113     for (int i=1;i<=n;++i)
    114         for (int j=1;j<=m;++j)
    115         {
    116             if (a[i][j]!='X') continue;
    117             for (int k=1;k<=8;++k)
    118             {
    119                 x=i+dx[k];
    120                 y=j+dy[k];
    121                 if (ok(x,y)&&a[x][y]=='X') return false;
    122             }
    123         }
    124     return true;
    125 }
    挫挫滴代码
  • 相关阅读:
    字符编码之间的转换 utf-8 , gbk等,(解决中文字符串乱码)
    信号分帧的三种实现方法及时间效率对比
    倒谱Cepstrum本质的理解
    Matlab 中 arburg 函数的理解与实际使用方法
    包络提取的两种方法-希尔伯特变换 和 局部峰值检测
    卡尔曼滤波的自我理解
    随机生成一个长度为n的数组
    JS 数字取整等操作
    vue 跳转路由新开页
    el-form 相关自定义校验
  • 原文地址:https://www.cnblogs.com/yoyoball/p/7571373.html
Copyright © 2011-2022 走看看