• [BZOJ2669][cqoi2012]局部极小值 状压dp+容斥原理


    2669: [cqoi2012]局部极小值

    Time Limit: 3 Sec  Memory Limit: 128 MB
    Submit: 1006  Solved: 539
    [Submit][Status][Discuss]

    Description

    有一个nm列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。
    给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

    Input

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

    Output

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

    Sample Input

    3 2
    X.
    ..
    .X

    Sample Output

    60

    HINT

     

    Source

    我们发现局部极小值不大于8个

    所以考虑状压dp。

    设f[i][S]表示从小向大放入了i个数字,局部极小值为S的情况下的方案数。

    显然我们要讨论当前第i个数放入的位置。

    如果放在了X位置,那么f[i][S]+=f[i-1][S-(1<<(j-1))]

    如果没放在X位置,那么根据定义,X位置和X位置周围的位置是不能放的,同事之前已经放了数的i-1个位置是不能放的。

    所以预处理每个状态可以放的位置数sta[S],f[i][j]=(f[i][S]+f[i-1][S]*max(sta[S]-i+1,0))

    然而这样算出的答案并不是正确答案,因为我们只保证了X位置为局部极小值,没保证非X位置不是局部极小值。

    所以利用容斥原理枚举那些非X位置是局部极小值后加加减减即可。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<algorithm>
     7 #define mod 12345678
     8 using namespace std;
     9 inline int read() {
    10     int x=0,f=1;char ch=getchar();
    11     for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    12     for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    13     return x*f;
    14 }
    15 int n,m,ans;
    16 int tx[8]={-1,-1,-1,0,0,1,1,1},ty[8]={-1,0,1,-1,1,-1,0,1};
    17 char s[30][30];
    18 int ax[30],ay[30],vis[30][30],sta[1000],f[30][1000];
    19 int solve() {
    20     int cnt=0;
    21     for(int i=1;i<=n;i++)
    22         for(int j=1;j<=m;j++) if(s[i][j]=='X') ax[++cnt]=i,ay[cnt]=j;
    23     int tot=(1<<cnt)-1;
    24     for(int i=0;i<=tot;i++) {
    25         memset(vis,0,sizeof(vis));
    26         for(int j=1;j<=cnt;j++) {
    27             if(i&(1<<(j-1))) continue;
    28             int x=ax[j],y=ay[j];
    29             vis[x][y]=1;
    30             for(int k=0;k<8;k++) {
    31                 int tox=x+tx[k],toy=y+ty[k];
    32                 vis[tox][toy]=1;
    33             }
    34         }
    35         int tmp=0;
    36         for(int j=1;j<=n;j++) for(int k=1;k<=m;k++) if(vis[j][k]) tmp++;
    37         sta[i]=n*m-tmp;
    38     }
    39     memset(f,0,sizeof(f));
    40     f[0][0]=1;
    41     for(int i=1;i<=n*m;i++) {
    42         for(int j=0;j<=tot;j++) {
    43             f[i][j]=(f[i][j]+f[i-1][j]*max(sta[j]-i+1,0))%mod;
    44             for(int k=1;k<=cnt;k++) {
    45                 if(j&(1<<(k-1))) {f[i][j]+=f[i-1][j-(1<<(k-1))];f[i][j]%=mod;}
    46             }
    47         }
    48     }
    49     return f[n*m][tot];
    50 }
    51 inline void dfs(int x,int y,int k) {
    52     if(y==m+1) {dfs(x+1,1,k);return;}
    53     if(x==n+1) {
    54         ans=ans+solve()*((k%2==0)?1:-1);ans%=mod;
    55         ans+=mod;ans%=mod;
    56         return;
    57     }
    58     dfs(x,y+1,k);
    59     bool flag=0;
    60     for(int i=0;i<8;i++) {
    61         int tox=x+tx[i],toy=y+ty[i];
    62         if(s[tox][toy]=='X') {flag=1;break;}
    63     }
    64     if(!flag&&s[x][y]!='X') {
    65         s[x][y]='X';
    66         dfs(x,y+1,k+1);
    67         s[x][y]='.';
    68     }
    69     return;
    70 }
    71 int main() {
    72     n=read(),m=read();
    73     for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    74     dfs(1,1,0);
    75     printf("%d
    ",((ans%mod)+mod)%mod);
    76 }
    View Code
  • 相关阅读:
    Canvas与Paint的0基础使用
    PHP经常使用功能
    java枚举在android项目应用
    POJ1833 &amp; POJ3187 &amp; POJ3785 next_permutation应用
    usb芯片调试经验
    Equals和==比較
    JSTL标准标签库具体解释
    零基础学python-3.3 标识符
    用react native 做的一个推酷client
    Linux异常关机后,Mysql启动出错ERROR 2002 (HY000)
  • 原文地址:https://www.cnblogs.com/wls001/p/9760012.html
Copyright © 2020-2023  润新知