Ural1519 Formula 1(插头dp)

原题网址:http://acm.timus.ru/problem.aspx?space=1&num=1519

有关插头dp和状态压缩请参考:
http://wenku.baidu.com/link?url=AFuYe_EfR5yXMNK0rY-TaLe6LLgKhsOVxBM1RQULxElPrvjQVlO724nUxlXtaDx4aLp7FHIz8AexYiTy06_r4CV5XUs6c9lM5vpz5kDr6HG

详细代码(c++):

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cstring>
  4 // using namespace std;
  5
  6 typedef long long LL;
  7 const int MAXRC = 15, MAXSTATE=300001;
  8 int m,n,er=-1,ec, idx=0;
  9 int city[MAXRC][MAXRC]={0}, bits_at[MAXRC];// 1 is valid.
 10
 11 class Hashmap{
 12 public:
 13     int head[MAXSTATE], nxt[MAXSTATE], size;
 14     LL state[MAXSTATE], num[MAXSTATE];
 15     void init(){
 16         size = 0;
 17         memset(head, -1, sizeof head);
 18         // memset(nxt, -1, sizeof nxt);// 头插法不用初始化nxt
 19     }
 20     void push(LL s, LL n){
 21         int pos = s%MAXSTATE, bg = head[pos];
 22         while(bg != -1){
 23             if(state[bg]==s){
 24                 num[bg] += n;
 25                 return ;
 26             }
 27             bg = nxt[bg];
 28         }
 29         state[size] = s;
 30         num[size] = n;
 31         // 头插法
 32         nxt[size] = head[pos];
 33         head[pos] = size++;
 34     }
 35 }hm[2];
 36
 37 inline int get_state_at(LL s, int j){
 38     return (s&bits_at[j])>>(j<<1);
 39 }
 40 inline LL set_state_at(LL s, int j, int b){
 41     s &= ~(bits_at[j]);
 42     return s|(b<<(j<<1));
 43 }
 44 inline LL set_state_at(LL s, int j, int bj, int bn){
 45     s &= ~(bits_at[j]+bits_at[j+1]);
 46     s |= (bj+(bn<<2))<<(j<<1);
 47     return s;
 48 }
 49 int find_match(LL s, int j){// c!=0
 50     int c = get_state_at(s, j), d = c == 1? 1:-1, f = 0;
 51     for(;;j+=d){
 52         if(get_state_at(s, j)==c) f++;
 53         else if(get_state_at(s,j)) f--;
 54         if(f == 0) return j;
 55     }
 56     return -1;
 57 }
 58 void dp(){
 59     idx = 0;hm[idx].init();hm[idx].push(0,1);
 60     for(int i=0; i<n; ++i){
 61         for(int j=0; j<hm[idx].size; ++j)
 62             hm[idx].state[j] <<= 2;// 最右位一定为0
 63         for(int j=0; j<m; ++j){
 64             int cur = idx^1;// 滚动数组,要求的状态集
 65             hm[cur].init();
 66             for(int k=0; k<hm[idx].size; ++k){
 67                 LL ps = hm[idx].state[k], pn = hm[idx].num[k];
 68                 int sl = get_state_at(ps, j), su = get_state_at(ps, j+1);
 69                 if(sl == 0 && su == 0){
 70                     if(!city[i][j]){// 将状态延伸到非 . 处
 71                         hm[cur].push(set_state_at(ps, j, 0, 0), pn);
 72                     }// 插头应该指向空白的格子
 73                     else if(city[i][j+1] && city[i+1][j]){
 74                         hm[cur].push(set_state_at(ps, j, 1, 2), pn);
 75                     }
 76                 }
 77                 // else if(!city[i][j]) continue;// 此处不会执行
 78                 else if(sl == 0 || su == 0){// 只延伸一个插头
 79                     if(city[i][j+1]) hm[cur].push(set_state_at(ps, j, 0, sl+su), pn);
 80                     if(city[i+1][j])
 81                         hm[cur].push(set_state_at(ps, j, sl+su, 0), pn);
 82                 }
 83                 else if(sl == su){// 合并连通块,同时左括号或右括号
 84                     int posl = find_match(ps, j), posu = find_match(ps, j+1);
 85                     int mn = std::min(posl, posu), mx = std::max(posl, posu);
 86                     LL cs = set_state_at(ps, mn, 1);
 87                     cs = set_state_at(cs, mx, 2);
 88                     hm[cur].push(set_state_at(cs, j, 0, 0), pn);
 89                 }
 90                 else if(sl == 2 && su == 1){// 合并成简单路径
 91                     hm[cur].push(set_state_at(ps, j, 0, 0), pn);
 92                 }
 93                 else if(i == er && j == ec){// 合并成回路,只在最后一个有效的格子
 94                     hm[cur].push(set_state_at(ps, j, 0, 0), pn);
 95                 }
 96             }
 97             idx = cur;// 交换状态
 98         }
 99     }
100 }
101 int main(){
102     freopen("in.txt", "r", stdin);
103     scanf("%d%d", &n, &m);
104     char cy[MAXRC][MAXRC];
105     for(int i=0; i<n; ++i){
106         scanf("%s", cy[i]);
107         for(int j=0; j<m; ++j){
108             if(cy[i][j] == ‘.‘){
109                 city[i][j] = 1;
110                 er = i; ec = j;
111             }
112         }
113     }
114     for(int i=0; i<=m; ++i){
115         bits_at[i] = 3<<(i<<1);// 0 is invalid, 1 is left bracket, 2 is right bracket.
116     }
117     dp();
118     printf("%lld\n", hm[idx].size>0?hm[idx].num[0]:0LL);
119     return 0;
120 }
时间: 2024-08-03 18:40:36

Ural1519 Formula 1(插头dp)的相关文章

[URAL1519] Formula 1 [插头dp入门]

题面: 传送门 思路: 先理解一下题意:实际上就是要你求这个棋盘中的哈密顿回路个数,障碍不能走 看到这个数据范围,还有回路处理,就想到使用插头dp来做了 观察一下发现,这道题因为都是回路,所以联通块上方的插头一定两两配对,可以使用括号序列代替最小表示法 分情况讨论一下 情况一:当前格子上方和左方都没有插头 这种时候可以继续,也可以给当前格子加一个下插头一个右插头,相当于一个新的联通分量 情况二:上方有一个下插头,左边没有 这时有两个决策:可以向右转,也可以继续向下,操作就是分别给这个格子一个右插

【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)

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】【插头dp模板】Formula 1

1519. Formula 1 Time limit: 1.0 second Memory limit: 64 MB Background Regardless of the fact, that Vologda could not get rights to hold the Winter Olympic games of 20**, it is well-known, that the city will conduct one of the Formula 1 events. Surely

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

插头DP小结

首先是CDQ<基于连通性状态压缩的动态规划问题>论文上的题目: URAL 1519 Formula 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 const int maxn = 15; 6 const int HASH = 30007; 7 const int SIZE = 1000010; 8 typedef long lon

插头dp的几个模板

/* ural1519 求经过全部可行点的哈密顿回路的个数 括号匹配法,转移有点复杂,可是时间空间比較小 */ #include<cstdio> #include<cstring> #include<string> #include<iostream> #include<algorithm> #include<cmath> #include<map> #include<queue> #define LL lon

插头DP专题

建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议先理解“插头”的概念.然后会HASH表(这个其实是很基础的东西,应该都会的).然后就是DP. 以及特殊题目的特殊处理. 好像一般是求N,M<=12的网格图的某种回路数或某种通路数的方案数. 大体上每个题说几句特殊处理,有问题请纠正....题目的顺序基本上难度递增 另外代码我都是用括号匹配的.因为感觉连通