URAL 1519 Formula 1

题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1519

陈丹琦的《基于连通性状态压缩的动态规划问题》的论文上的题目

题意:

给你m*n的棋盘,有的格子是障碍,问共有多少条回路使得经过每个非障碍格子恰好一次。

做法:

论文上的思路讲的很清楚了,这里用最小表示法来做。

因为(2 ≤ NM ≤ 12),所以最多出现6个联通块,所以用8进制来做。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 int N, M;
  4 #define maxn 15
  5 #define HASH 30007
  6 #define STATE 200010
  7 int mp[maxn][maxn];
  8 int pos[maxn];
  9 int mark, ex, ey;
 10 int code[maxn];
 11 struct HashMap
 12 {
 13     int head[HASH], next[STATE], cnt;
 14     long long state[STATE];
 15     long long sum[STATE];
 16     void init()
 17     {
 18         cnt = 0;
 19         memset(head, -1, sizeof(head));
 20     }
 21     void push(long long sta, long long val)
 22     {
 23         int h = sta%HASH;
 24         for(int i = head[h]; i != -1; i = next[i])
 25         {
 26             if(state[i] == sta)  //如果这种状态已经存在
 27             {
 28                 sum[i] += val;
 29                 return;
 30             }
 31         }
 32         state[cnt] = sta;
 33         sum[cnt] = val;
 34         next[cnt] = head[h];
 35         head[h] = cnt++;
 36     }
 37 }hashmp[2];//滚动数组
 38 void decode(int *code, long long sta)
 39 {
 40     for(int i = M; i >= 0; i--)
 41     {
 42         code[i] = sta&7;
 43         sta >>= 3;
 44     }
 45 }
 46 long long encode(int *code) //转化成2进制
 47 {
 48     int cnt = 1;
 49     int vis[maxn];
 50     memset(vis, -1, sizeof(vis));
 51     vis[0] = 0;  //0不连通
 52     long long sta = 0;
 53
 54     for(int i = 0; i <= M; i++)
 55     {
 56         if(vis[code[i]] == -1) vis[code[i]] = cnt++;
 57         code[i] = vis[code[i]];
 58         sta <<= 3;
 59         sta |= code[i];
 60     }
 61     return sta;
 62 }
 63 void AtLast(int *code)
 64 {
 65     for(int i = M; i > 0; i--) code[i] = code[i-1];
 66     code[0] = 0;
 67 }
 68 void slove()
 69 {
 70     mark = 0;
 71     hashmp[mark].init();
 72     hashmp[mark].push(0, 1);       //开始时轮廓线在最上方
 73
 74     for(int i = 1; i <= N; i++)
 75     {
 76         for(int j = 1; j <= M; j++)
 77         {
 78             hashmp[mark^1].init();//初始化
 79             if(mp[i][j] == 0)     //不能放置
 80             {
 81                 for(int k = 0; k < hashmp[mark].cnt; k++)
 82                 {
 83                     decode(code, hashmp[mark].state[k]);
 84                     code[j-1] = code[j] = 0;
 85                     if(j == M) AtLast(code);
 86                     hashmp[mark^1].push(encode(code),hashmp[mark].sum[k]);
 87                 }
 88             }
 89             else                //可以放置
 90             {
 91                 for(int k = 0; k < hashmp[mark].cnt; k++)
 92                 {
 93                     decode(code, hashmp[mark].state[k]);
 94                     int left, up;
 95                     left = code[j-1]; up = code[j];
 96                     if(left == 0 && up == 0)            //情况1:没有上插头和左插头,新建一个联通分量
 97                     {
 98                         if(mp[i+1][j] && mp[i][j+1])
 99                         {
100                             code[j-1] = code[j] = 13;  //如果mp[i][j+1] = 1,则不可能出现j == M的情况
101                             hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
102                         }
103                     }
104                     else if(left && up)                //情况2:有上插头和左插头
105                     {
106                         if(left != up)                 //上插头和左插头不联通,合并联通分量。
107                         {
108                             code[j-1] = code[j] = 0;   //则不可能再有右插头和下插头
109                             for(int ii = 0; ii <= M; ii++)
110                             {
111                                 if(code[ii] == up) code[ii] = left;
112                             }
113                             if(j == M) AtLast(code);
114                             hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
115                         }
116                         else if(left == up)            //上插头和左插头联通
117                         {
118
119                             if(i == ex && j == ey)     //这种情况只可能出现在最后一个可以摆放的位置
120                             {
121                                 code[j-1] = code[j] = 0;
122                                 if(j == M) AtLast(code);
123                                 hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
124                             }
125                         }
126                     }
127                     else if((left&&(!up)) || (up&&(!left)))//情况3:上插头和左插头只存在一个
128                     {
129                         int val;
130                         if(left) val = left;
131                         else val = up;
132                         if(mp[i][j+1])     //右边的格子可走
133                         {
134                             code[j] = val; code[j-1] = 0;
135                             hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
136                         }
137                         if(mp[i+1][j])    //下面的格子可走
138                         {
139                             code[j-1] = val; code[j] = 0;
140                             if(j == M) AtLast(code);
141                             hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
142                         }
143                     }
144                 }
145             }
146             mark^=1;
147         }
148     }
149 }
150 int main()
151 {
152    // freopen("in.txt", "r", stdin);
153    //  freopen("out.txt", "w", stdout);
154     while(~scanf("%d%d", &N, &M))
155     {
156         memset(mp, 0, sizeof(mp));
157         bool flag = false;
158         for(int i = 1; i <= N; i++)
159         {
160             char str[15];
161             scanf("%s", str);
162             for(int j = 0; j < M; j++)
163             {
164                 if(str[j] == ‘.‘)
165                 {
166                     mp[i][j+1] = 1;
167                     ex = i; ey = j+1;
168                     flag = true;
169                 }
170                 else if(str[j] == ‘*‘) mp[i][j+1] = 0;
171             }
172         }
173         if(flag == false)  //没有空块
174         {
175             printf("0\n"); continue;
176         }
177         slove();
178         long long ans = 0;;
179         for(int i = 0; i < hashmp[mark].cnt; i++)
180         {
181             ans += hashmp[mark].sum[i];
182         }
183         printf("%lld\n", ans);
184     }
185     return 0;
186 }
时间: 2024-10-19 22:31:04

URAL 1519 Formula 1的相关文章

bzoj1814: Ural 1519 Formula 1 2011-12-20

1814: Ural 1519 Formula 1Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 263  Solved: 70[Submit][Status][Discuss]DescriptionRegardless of the fact, that Vologda could not get rights to hold the Winter Olympic games of 20**, it is well- known, that the 

【插头DP】URAL 1519 Formula 1

通道:http://acm.timus.ru/problem.aspx?space=1&num=1519 题意:单回路,经过全部可达点,有阻碍点. 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = 13; const int MAX_M = 13; const int HASH = 10007; const in

bzoj 1814: Ural 1519 Formula 1 插头dp经典题

用的括号序列,听说比较快. 然并不会预处理,只会每回暴力找匹配的括号. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define N 199917 6 #define ll long long 7 #define bp 1<<bit[j-1] 8 #define bq 1<<bit[j] 9 using nam

URAL - 1519 Formula 1 (插头DP)

这里写链接内容 刚开始学插头dp好吃力,看了别人的代码有点看不懂,所以就参考了别人的代码,写了注释,希望有帮助 如果看不懂可以问 //下面所说的情况全在论文中的第13页 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 13 #define S1 14000 #define S2 1600000 struct Queue{ int opt; long

URAL 1519 Formula 1 (插头DP,常规)

题意:给一个n*m的矩阵,格子中是'*'则是障碍格子,不允许进入,其他格子都是必走的格子,所走格子形成一条哈密顿回路,问有多少种走法? 思路: 本来是很基础的题,顿时不知道进入了哪个坑.这篇插头DP的文章够详细,推荐一看(最好不要照抄代码). 细节要小心,比如输出用I64d,必须用long long,判断是否无哈密顿回路,位操作等等. 这次仍然用括号表示法,1表示(,2表示). 1 #include <bits/stdc++.h> 2 #define pii pair<int,int&g

[URAL 1519] Formula 1 轮廓线DP 括号序列

题意 n * m 的矩形, 有坏点, 问哈密顿回路数量. n, m <= 11 . 分析 1. 确立状态 我们考虑轮廓线DP. 为此, 我们要刻画并量化轮廓线的相关信息: ① 插头是否存在? ② 插头的连通性. 我们发现: 插头一一对应, 且互不相交. 于是考虑使用括号序列刻画轮廓线. 至于量化, 我们将一个括号序列当做一个三进制数即可. 2. 转移 从上一行的最后一个, 转移到当前行的第一个: 位移. 当前格子为坏点: 对于没有插头插到当前点的状态原样复制. 否则: (1) L = # , R

【bzoj1814】Ural 1519 Formula 1 插头dp

题目描述 一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数. 输入 The first line contains the integer numbers N and M (2 ≤ N, M ≤ 12). Each of the next N lines contains M characters, which are the corresponding cells of the rectangle. Character "." (full stop)

bzoj1814: Ural 1519 Formula 1 动态规划 插头dp

dbzoj依然爆炸 题目描述 一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数. 输入 The first line contains the integer numbers N and M (2 ≤ N, M ≤ 12). Each of the next N lines contains M characters, which are the corresponding cells of the rectangle. Character "." (f

bzoj 1814: Ural 1519 Formula 1【插头dp】

设f[i][j][s]为轮廓线推到格子(i,j),状态为s的方案数 括号表示一段线的左端和右端,表示成左括号和右括号,状压的时候用1和2表示,0表示已经闭合 下面的蓝线是黄色格子的轮廓线,dp转移要把它转到橙色轮廓线,设已经在状压的s中取到两条边的状态记为b1,b2 然后分很多情况讨论: (i,j)是障碍:那就只能什么都不放的转移,也就是只能从b1=0,b2=0转移到新轮廓线的b1=0,b2=0 if(!a[i][j]) { if(!b1&&!b2) add(x,v); } b1=0,b2