zoukankan      html  css  js  c++  java
  • 【插头DP】BZOJ1814-Formula

    【题目大意】

    给出一个m*n的矩阵里面有一些格子为障碍物,求经过所有非障碍格子的哈密顿回路个数。

    【思路】

    最典型的插头DP。分为三种情况:

    (1)当前格子既没有上插头也没有左插头。

    如果下边和右边都没有障碍,新建连同分量。

    (2)如果只有左插头或者右插头。

    延伸或者拐弯,当然也要判断有没有障碍。

    (3)上插头和左插头都没有。

    1. 如果两个插头不连通(编号不一样),那么将两个插头所处的连通分量合并,标记相同的连通块标号,O(n)扫描保证最小表示;
    2. 如果已经连通,相当于出现了一个回路,这种情况只能出现在最后一个非障碍格子。

    由于状态非常多,用hash表存储状态。

    decode和encode注意一下,这里不赘述了。

    【错误点】

    注意一下ch要开得够大,具体见代码。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<vector>
      6 using namespace std;
      7 typedef long long ll;
      8 const int MAXN=14;
      9 const int HASH=30007;
     10 int ex,ey;
     11 int m,n;
     12 int maze[MAXN][MAXN];
     13 int code[MAXN],ch[MAXN];
     14 struct HashMap
     15 {
     16     vector<int> hash[HASH];//存储f和state的下标 
     17     vector<ll> f,state;//存储对应的方案数和状态 
     18     void init()
     19     {
     20         for (int i=0;i<HASH;i++) vector<int>().swap(hash[i]);
     21         vector<ll>().swap(f);
     22         vector<ll>().swap(state);
     23     }
     24     void push(ll st,ll ans)
     25     {
     26         int h=st%HASH;
     27         for (int i=0;i<hash[h].size();i++)
     28         {
     29             int now=hash[h][i];
     30             if (state[now]==st)//如果已经存储了当前状态,直接累加 
     31             {
     32                 f[now]+=ans;
     33                 return;
     34             }
     35         }
     36         //如果没有存储过当前状态,累加 
     37         state.push_back(st);
     38         f.push_back(ans);
     39         hash[h].push_back(state.size()-1);
     40     }
     41 }dp[2];
     42 
     43 void decode(ll st)
     44 {
     45     memset(code,0,sizeof(code));
     46     for (int i=n;i>=0;i--)
     47     {
     48         code[i]=st&7;//每三位代表一个信息 
     49         st>>=3;
     50     }
     51 }
     52 
     53 ll encode()
     54 //用最小表示法重新编码 
     55 {
     56     int cnt=1;
     57     memset(ch,-1,sizeof(ch));
     58     ch[0]=0;
     59     long long st=0;
     60     for (int i=0;i<=n;i++)
     61     {
     62         if (ch[code[i]]==-1) ch[code[i]]=cnt++;
     63         code[i]=ch[code[i]];
     64         st<<=3;
     65         st|=code[i]; 
     66     } 
     67     return st;
     68 }
     69 
     70 void shift()
     71 {
     72     for (int i=n;i>0;i--) code[i]=code[i-1];
     73     code[0]=0;
     74 }
     75 
     76 
     77 void dpblank(int i,int j,int cur)
     78 {
     79     for (int k=0;k<dp[1-cur].state.size();k++)
     80     {
     81         decode(dp[1-cur].state[k]);
     82         int left=code[j-1];//左插头 
     83         int up=code[j];//上插头
     84         
     85         /*如果上下插头都没有*/ 
     86         if (!left && !up)
     87         {
     88             if (maze[i][j+1] && maze[i+1][j])
     89             {
     90                 code[j-1]=code[j]=MAXN-1;
     91                 //这里只要随便设置一个大数即可
     92                 
     93                 //【attention】这里千万不可以设置成MAXN,否则ch数组会抱★★★★★★★★
     94                 
     95                 //因为encode会重新用最小表示法编码 
     96                 dp[cur].push(encode(),dp[1-cur].f[k]);
     97             }
     98         }
     99         
    100         /*只有上插头或者只有左插头*/
    101         if ((left&&(!up))||((!left)&&up))
    102         {
    103             
    104             int t=left|up;
    105             if (maze[i][j+1])//右边没有障碍
    106             {
    107                 code[j-1]=0;
    108                 code[j]=t;
    109                 dp[cur].push(encode(),dp[1-cur].f[k]);
    110             } 
    111             if (maze[i+1][j])//下面没有障碍
    112             {
    113                 code[j-1]=t;
    114                 code[j]=0;
    115                 if (j==n) shift();
    116                 dp[cur].push(encode(),dp[1-cur].f[k]);
    117             }
    118         }
    119         
    120         /*上插头和右插头都有*/
    121         if (left && up)
    122         {
    123             if (left==up)
    124             {
    125                 if (i==ex && j==ey)
    126                 {
    127                     code[j-1]=code[j]=0;
    128                     if (j==n) shift();
    129                     dp[cur].push(encode(),dp[1-cur].f[k]);
    130                 }
    131             }
    132             else
    133             {
    134                 code[j-1]=code[j]=0;
    135                 for (int t=0;t<=n;t++)
    136                     if (code[t]==up) code[t]=left;
    137                 if (j==n) shift();
    138                 dp[cur].push(encode(),dp[1-cur].f[k]);
    139             }
    140         } 
    141     }
    142 }
    143 
    144 void dpblock(int i,int j,int cur)
    145 {
    146     int k=0;
    147     for (int k=0;k<dp[1-cur].state.size();k++)
    148     {
    149         decode(dp[1-cur].state[k]);
    150         code[j-1]=code[j]=0;
    151         if (j==n) shift();
    152         dp[cur].push(encode(),dp[1-cur].f[k]);
    153     }
    154 }
    155 
    156 void solve()
    157 {
    158     int cur=0;
    159     ll ans=0;
    160     dp[cur].init();
    161     dp[cur].push(0,1);//DP数组初始化 
    162     for (int i=1;i<=m;i++)
    163         for (int j=1;j<=n;j++)
    164         {
    165             cur^=1;
    166             dp[cur].init();
    167             if (maze[i][j]) dpblank(i,j,cur);
    168                 else dpblock(i,j,cur);172         }
    173     for (int i=0;i<dp[cur].state.size();i++)
    174         ans+=dp[cur].f[i];
    175     printf("%lld",ans);
    176 }
    177 
    178 void init()
    179 {
    180     memset(maze,0,sizeof(maze));
    181     ex=ey=0;
    182     for (int i=1;i<=m;i++)
    183     {
    184         char str[MAXN];
    185         scanf("%s",str);
    186         for (int j=0;j<n;j++)
    187         {
    188             if (str[j]=='.')
    189             {
    190                 ex=i;
    191                 ey=j+1;
    192                 maze[i][j+1]=1;
    193             }
    194         }
    195     }
    196 }
    197 
    198 int main()
    199 {
    200     while (scanf("%d%d",&m,&n)!=EOF)
    201     {
    202         init();
    203         if (ex==0) puts("0");//如果没有一个是空格的话直接输出0 
    204             else solve();
    205     }
    206     return 0;    
    207 } 
  • 相关阅读:
    制作自己的Docker镜像
    Docker 常见应用部署
    一文读懂Docker相关命令
    linux在下软件太卡?手把手教你配置国内镜像源
    2013年蓝桥杯省赛C组笔记
    java基本数据类型之间的转换
    h5中的分组元素figure、figcaption、hgroup元素介绍
    初识WSGI接口
    h5中的结构元素header、nav、article、aside、section、footer介绍
    提交 linux kernel 补丁流程备忘录
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/5846878.html
Copyright © 2011-2022 走看看