zoukankan      html  css  js  c++  java
  • BZOJ 2669 CQOI2012 局部极小值 状压dp+容斥原理

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2669

    题意概述:实际上原题意很简洁了我就不写了吧。。。。

    二话不说先观察一下性质,首先棋盘很窄,可以乱搞的样子,然后注意到如果一个点是局部极小值那么周围3*3矩阵内不能有另一个局部最小值。于是画个图发现题目的数据范围最多有8个局部最小值。性质大概就是这些了。

    暴力实际上是搜索,本质是多阶段决策问题。由于棋盘很小,容易让人联想到搞个插头dp之类东西来弄一下,依次填每个格子来作为一个决策阶段。然后就发现。。。这个东西太复杂了。。。等你想出来不知道什么时候去了(而且很有可能想不出来)。。。于是需要换个思路,把决策阶段改一下,每一行作为阶段的话更加复杂。。。弃疗。那么决策阶段很有可能就不是按照格子的顺序来的了。注意到局部最小值一定是周围的格子里面最小的那个,并且最多只有8个局部最小值,那么尝试从小到大填充每一个数字作为阶段,把局部最小值是否填充的状态压进去。

    于是令f(s,i)表示用1~i的数字填充棋盘,集合s中的局部最小值已经被填充的方案数。

    分析这种决策下的性质,发现一个局部最小值一定是以其为中心3*3内最先填的那个,也就是说如果一个局部最小值没有填充,那么周围3*3的点都不能填充。排除所有不可以填充的点剩下的就是可以填充的点,令cnt[s]表示局部极小值填充状态为s时的棋盘上最多有几个数字。

    得到f(s,i)=f(s,i-1)*C(cnt[s]-i+1,1)+sum{ f(s-{j},i-1) | j是s的子集 },时间复杂度O(N*M*X*2^X),X表示棋盘上有多少个局部极小值。

    但是可以注意到在状态设计的时候可以保证题目给出的X都成为局部最小值,但是可能让不是局部最小值的位置变成局部最小值。

    于是这题最神的地方来了:容斥!由于棋盘很小,所以说打个回溯跑一下局部极小值的分布位置发现最多也就16000多的方案。我们用回溯跑出每一种题目要求位置为局部极小值的棋盘状态,对于每个状态来一次dp,然后根据有多少个额外的点是局部极小值进行奇偶容斥就得出答案。

    时间复杂度O(O(容斥)*N*M*X*2^X)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<algorithm>
     6 #include<cmath>
     7 #include<queue>
     8 #include<set>
     9 #include<map>
    10 #include<vector>
    11 #include<cctype>
    12 using namespace std;
    13 const int maxn=6;
    14 const int maxm=9;
    15 const int maxs=(1<<8)+2;
    16 const int mo=12345678;
    17 
    18 int N,M,X,ans;
    19 char mp[maxn][maxm];
    20 struct XY{ int x,y; }p[10];
    21 int bin[10],f[maxs][4*7+5],cnt[maxs],vis[maxn][maxm],sz[maxs];
    22 
    23 void data_in()
    24 {
    25     scanf("%d%d",&N,&M);
    26     for(int i=1;i<=N;i++){
    27         scanf("%s",mp[i]+1);
    28         for(int j=1;j<=M;j++)
    29             if(mp[i][j]=='X') p[++X]=(XY){i,j};
    30     }
    31     for(int i=1;i<=9;i++) bin[i]=1<<i-1;
    32     for(int s=1;s<bin[9];s++) sz[s]=sz[s>>1]+(s&1);
    33 }
    34 void dp(int tot)
    35 {
    36     for(int s=0;s<bin[tot+1];s++){
    37         cnt[s]=0;
    38         memset(vis,0,sizeof(vis));
    39         for(int j=1;j<=tot;j++) if(!(bin[j]&s)){
    40             int x=p[j].x,y=p[j].y;
    41             vis[x-1][y-1]=vis[x-1][y]=vis[x-1][y+1]=
    42             vis[x][y-1]=vis[x][y]=vis[x][y+1]=
    43             vis[x+1][y-1]=vis[x+1][y]=vis[x+1][y+1]=1;
    44         }
    45         for(int i=1;i<=N;i++)
    46         for(int j=1;j<=M;j++) cnt[s]+=1-vis[i][j];
    47     }
    48     memset(f,0,sizeof(f));
    49     f[0][0]=1;
    50     for(int i=1;i<=cnt[0];i++)
    51         f[0][i]=f[0][i-1]*(cnt[0]-i+1)%mo;
    52     for(int s=1;s<bin[tot+1];s++)
    53     for(int i=sz[s];i<=cnt[s];i++){
    54         f[s][i]=f[s][i-1]*(cnt[s]-i+1)%mo;
    55         for(int j=1;j<=tot;j++) if(bin[j]&s)
    56             f[s][i]=(f[s][i]+f[s^bin[j]][i-1])%mo;
    57     }
    58 }
    59 bool check(int x,int y) { return mp[x-1][y-1]!='X'&&mp[x-1][y]!='X'&&mp[x-1][y+1]!='X'&&mp[x][y-1]!='X'; }
    60 void dfs(int x,int y,int n)
    61 {
    62     if(x>N){
    63         dp(X+n);
    64         if(n%2==0) ans=(ans+f[bin[X+n+1]-1][N*M])%mo;
    65         else ans=(ans-f[bin[X+n+1]-1][N*M]+mo)%mo;
    66         return;
    67     }
    68     int xx=x,yy=y+1;
    69     if(yy>M) xx++,yy=1;
    70     if(mp[x][y]=='X'){
    71         if(check(x,y)) dfs(xx,yy,n);
    72     }
    73     else{
    74         if(check(x,y)){
    75             mp[x][y]='X',p[X+n+1]=(XY){x,y};
    76             dfs(xx,yy,n+1);
    77             mp[x][y]='.';
    78         }
    79         dfs(xx,yy,n);
    80     }
    81 }
    82 void work()
    83 {
    84     dfs(1,1,0);
    85     printf("%d
    ",ans);
    86 }
    87 int main()
    88 {
    89     data_in();
    90     work();
    91     return 0;
    92 }
  • 相关阅读:
    Linux 下的类似Windows下Everything的搜索工具
    windows和linux环境下制作U盘启动盘
    程序调试手段之gdb, vxworks shell
    LeetCode 1021. Remove Outermost Parentheses (删除最外层的括号)
    LeetCode 1047. Remove All Adjacent Duplicates In String (删除字符串中的所有相邻重复项)
    LeetCode 844. Backspace String Compare (比较含退格的字符串)
    LeetCode 860. Lemonade Change (柠檬水找零)
    LeetCode 1221. Split a String in Balanced Strings (分割平衡字符串)
    LeetCode 1046. Last Stone Weight (最后一块石头的重量 )
    LeetCode 746. Min Cost Climbing Stairs (使用最小花费爬楼梯)
  • 原文地址:https://www.cnblogs.com/KKKorange/p/8478183.html
Copyright © 2011-2022 走看看