连通性状态压缩题解-simple & naive版

suffix array总算搞定了,集训还剩两周时间,搞一下插头DP

先挖个坑,这里将持续update一些插头的题解。

HDU 1693 Eat the Trees

简单多回路问题,多回路的优点就是简单,不需要维护连通性,之需要维护轮廓线上的插头即可。

这题最为新手入门题,包括了插头需要注意的几点事项。

第一点:按格转移比较快。

第二点:在按格转移过程之中,一定要注意当前格的左插头和上插头,由于是多回路,所以比较简单,在单回路问题上,需要对插头分类讨论,如果有独立插头讨论更加复杂。

第三点:如果有障碍物,讨论是需要注意,尤其是后面将要介绍的最小表示法中,对障碍物(或者是可不选取的格子)需要精确的讨论转移。

第四点:行间转移,在一行行末向下一行行初转移的过程中,需要注意行末位置的轮廓线上的最后一个位置(是一个左插头位置)不能有有插头。而行初位置的插头也不能有。所以转移时,要把行末的有效状态乘以一个进制数(X进制左移一位)。

代码如下:

hdu 1693

然后是URAL 1519以及POJ 1739,后者也是男人8题之一。

这两道题是最简单的括号序列表示法,我们采用三进制状态压缩。

URAL求回路个数。依然是记录轮廓线上的状态。我们用0表示没有括号,1表示左括号,2表示右括号。并用左位表示该格子左边位置插头状态,上位表示该格子上面位置的插头状态。

1.首先预处理出所以合法状态,合法状态是插头不能交叉,而且每个右插头有匹配的左插头。

2.依然是按格转移,由于要维护括号匹配,插头合并,有的时候需要重新整理括号,转移的时候分情况讨论。

情况1.该节点不可通过,这时只有当前块的左位和上位头全部是0才可以转移。

情况2.该节点可以通过,且左位和上位全部是0。该情况在,可以新建立1对匹配的括号序列.

情况3.该节点可以通过,且左位和上位全部是1。这时要合并2个左括号,而且重新整理插头状态,找到右边第一个独立的右括号,把他变成左括号。

情况4.该节点可以通过,且左位和上位全部是2。这时要合并2个右括号,而且重新整理插头状态,找到左边第一个独立的左括号,把他变成右括号。

情况5.该节点可以通过,且左位和上位分别是右括号和左括号,这是合并2个括号,则不需要重新整理插头。

情况6.该节点可以通过,且左位和上位一个是0,一个不是0,这是又2种扩展,为一下格增加的左插头,或者为下一行增加一个上插头。

情况7.只有在最后一个合法的格子发生,就是合并一个左括号和一个右括号。

清楚的分析了上述7种情况以后,就可以直白的DP转移了(虽然有些不好写……不过在凶残的今天这个已经是入门级别的插头了……)。

本题还可以用hash优化,但是并没有使用,导致每次memset的状态过多,浪费了一定实践,将在后面的最小表示法中,描述hash优化。

代码如下:

ural 1519
  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdio>
  4 using namespace std;
  5 #define REP(i, l, n) for(int i = l;i < n;i++)
  6 #define CC(con, i) memset(con, i, sizeof(con))
  7 typedef long long LL;
  8 const int N = 14;
  9 const int TOT = 50000;
 10 const int MAXN = 1594323;// 3^13
 11 char maps[N][N];
 12 int bit3[N] = {1}, status[TOT];
 13 int Hash[MAXN], allS = 0;
 14 LL dp[2][TOT];
 15 bool check(int s)
 16 {
 17     int cnt = 0;
 18     while(s)
 19     {
 20         int n = s % 3;
 21         if(n == 1) cnt++;
 22         if(n == 2) cnt--;
 23         if(cnt < 0) return false;
 24         s /= 3;
 25     }
 26     return (cnt == 0);
 27 }
 28 void preprocess()
 29 {
 30     REP(i, 1, N) bit3[i] = bit3[i - 1] * 3;
 31     REP(i, 0, bit3[N - 1])
 32     {
 33         if(check(i))
 34         {
 35             Hash[i] = allS;
 36             status[allS++] = i;
 37         }
 38         else Hash[i] = -1;
 39     }
 40     status[allS] = MAXN;
 41 }
 42 int getbit(int s, int i)
 43 {
 44     while(i-- > 0) s /= 3;
 45     return s % 3;
 46 }
 47 void transfer(LL& dest, LL add)
 48 {
 49     dest == -1 ? (dest = add) : (dest += add);
 50 }
 51 LL DP(int n, int m, int px, int py)
 52 {
 53     LL ans = 0;
 54     int now = 0, pre;
 55     CC(dp, -1);
 56     dp[0][0] = 1;
 57     for(int i = 0; i < n; i++)
 58     {
 59         for(int j = 0; j < m; j++)
 60         {
 61             pre = now;now ^= 1;
 62             CC(dp[now], -1);
 63             for(int k = 0, s; s = status[k], s < bit3[m + 1]; k++)
 64             {
 65                 if(dp[pre][k] == -1) continue;
 66                 int l = getbit(s, j), u = getbit(s, j + 1);
 67                 int nows = s - l * bit3[j] - u * bit3[j + 1];
 68                 if(maps[i][j] == '*')
 69                 {
 70                     if(l == 0 && u == 0)
 71                         transfer(dp[now][k], dp[pre][k]);
 72                 }
 73                 else if(l == 0 && u == 0)//both down and right build 2 plugin
 74                 {
 75                     if(maps[i][j + 1] == '.' && maps[i + 1][j] == '.')
 76                     {
 77                         int nxt = nows + bit3[j] + 2 * bit3[j + 1];
 78                         transfer(dp[now][Hash[nxt]], dp[pre][k]);
 79                     }
 80                 }
 81                 else if(l == 1 && u == 1)// merge (( make )) to ()
 82                 {
 83                     int cnt = 0;
 84                     for(int b = j + 2; b <= m; b++)
 85                     {
 86                         int tmp = getbit(nows, b);
 87                         if(tmp == 2) cnt--;
 88                         if(tmp == 1) cnt++;
 89                         if(cnt == -1)
 90                         {
 91                             transfer(dp[now][Hash[nows - bit3[b]]], dp[pre][k]);
 92                             break;
 93                         }
 94                     }
 95                 }
 96                 else if(l == 2 && u == 2)// merge )) make (( to ()
 97                 {
 98                     int cnt = 0;
 99                     for(int b = j - 1;b >= 0;b--)
100                     {
101                         int tmp = getbit(nows, b);
102                         if(tmp == 1) cnt++;
103                         if(tmp == 2) cnt--;
104                         if(cnt == 1)
105                         {
106                             transfer(dp[now][Hash[nows + bit3[b]]], dp[pre][k]);
107                             break;
108                         }
109                     }
110                 }
111                 else if(l == 1 && u == 2)//merge () at last grid
112                 {
113                     if(px == i && py == j)
114                         ans += dp[pre][k];
115                 }
116                 else if(l == 2 && u == 1)//merge )(
117                 {
118                     transfer(dp[now][Hash[nows]], dp[pre][k]);
119                 }
120                 else if((!l && u) || (l && !u))
121                 {
122                     if(maps[i + 1][j] == '.')
123                         transfer(dp[now][Hash[nows + (l + u) * bit3[j]]], dp[pre][k]);
124                     if(maps[i][j + 1] == '.')
125                         transfer(dp[now][Hash[nows + (l + u) * bit3[j + 1]]], dp[pre][k]);
126                 }
127             }
128         }
129         pre = now;now ^= 1;
130         CC(dp[now], -1);//must CC -1
131         for(int k = 0, s; s = status[k], s < bit3[m]; k++)
132             if(dp[pre][k] != -1)
133                 dp[now][Hash[s * 3]] = dp[pre][k];
134     }
135     return ans;
136 }
137 int main()
138 {
139     int n, m, px, py;
140     preprocess();
141     while(scanf("%d %d", &n, &m) == 2)
142     {
143         CC(maps, 0);
144         REP(i, 0, n) scanf("%s", maps[i]);
145         REP(i, 0, n) REP(j, 0, m) if(maps[i][j] == '.') px = i, py = j;
146         printf("%lld\n", DP(n, m, px, py));
147     }
148     return 0;
149 }

而对于POJ 1739。

我们可以通过构造方法,再外围加上1圈墙,然后改造成 ural 1519的方法解决问题。但是速度较慢,因为扩展的原因,将导致状态增多。复杂度遍高。实际上本题又更好的方法,因为题目要求从最后一行的第一个格子走到最后一行的最后一个格子。所以我们最后只需要统计最终状态为01000002的个数既可。同时还避免了情况7个讨论。

而且这样处理后状态极少,每格的合法状态只有不到1000个,复杂度大大降低了,也不许要用hash之类的优化了。

代码如下:

POJ 1739
  1 #include <cstring>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <string>
  5 #include <iostream>
  6 using namespace std;
  7 #define CC(i, v) memset(i, v, sizeof(i))
  8 #define REP(i, l, n) for(int i = l;i < int(n);++i)
  9 typedef long long LL;
 10 const int N = 10;
 11 const int TOT = 1000;
 12 const int MAXN = 1594323;// 3^13
 13 char maps[N][N];
 14 int bit3[N] = {1}, status[TOT];
 15 int Hash[MAXN], allS = 0;
 16 LL dp[2][TOT];
 17 bool check(int s) {
 18     int cnt = 0;
 19     while(s) {
 20         int n = s % 3;
 21         if(n == 1) cnt++;
 22         if(n == 2) cnt--;
 23         if(cnt < 0) return false;
 24         s /= 3;
 25     }
 26     return (cnt == 0);
 27 }
 28 void preprocess() {
 29     REP(i, 1, N) bit3[i] = bit3[i - 1] * 3;
 30     REP(i, 0, bit3[N - 1]) {
 31         if(check(i)) {
 32             Hash[i] = allS;
 33             status[allS++] = i;
 34         } else {
 35             Hash[i] = -1;
 36         }
 37     }
 38     status[allS] = MAXN;
 39 }
 40 int getbit(int s, int i) {
 41     return s / bit3[i] % 3;
 42 }
 43 void transfer(LL& dest, LL add) {
 44     dest == -1 ? (dest = add) : (dest += add);
 45 }
 46 LL DP(int n, int m) {
 47     int now = 0, pre = 1;
 48     CC(dp, -1);
 49     dp[0][0] = 1;
 50     for(int i = 0; i < n; i++) {
 51         for(int j = 0; j < m; j++) {
 52             swap(now, pre);
 53             CC(dp[now], -1);
 54             for(int k = 0, s; s = status[k], s < bit3[m + 1]; k++) {
 55                 if(dp[pre][k] == -1) continue;
 56                 int l = getbit(s, j), u = getbit(s, j + 1);
 57                 int nows = s - l * bit3[j] - u * bit3[j + 1];
 58                 if(maps[i][j] != '.') {
 59                     if(l == 0 && u == 0) {
 60                         transfer(dp[now][k], dp[pre][k]);
 61                     }
 62                 } else if(l == 0 && u == 0) { //当向下向右都可以走的时候建立两个插头
 63                     if(maps[i][j + 1] == '.' && maps[i + 1][j] == '.') {
 64                         int nxt = nows + bit3[j] + 2 * bit3[j + 1];
 65                         transfer(dp[now][Hash[nxt]], dp[pre][k]);
 66                     }
 67                 } else if(l == 1 && u == 1) { //合并2个左插头,整理相应的右插头
 68                     int cnt = 0;
 69                     for(int b = j + 2; b <= m; b++) {
 70                         int tmp = getbit(nows, b);
 71                         if(tmp == 2) cnt--;
 72                         if(tmp == 1) cnt++;
 73                         if(cnt == -1) {
 74                             transfer(dp[now][Hash[nows - bit3[b]]], dp[pre][k]);
 75                             break;
 76                         }
 77                     }
 78                 } else if(l == 2 && u == 2) { //合并2个右插头,整理相应的左插头
 79                     int cnt = 0;
 80                     for(int b = j - 1;b >= 0;b--) {
 81                         int tmp = getbit(nows, b);
 82                         if(tmp == 1) cnt++;
 83                         if(tmp == 2) cnt--;
 84                         if(cnt == 1) {
 85                             transfer(dp[now][Hash[nows + bit3[b]]], dp[pre][k]);
 86                             break;
 87                         }
 88                     }
 89                 } else if(l == 2 && u == 1) { //合并)(,什么都不用整理
 90                     transfer(dp[now][Hash[nows]], dp[pre][k]);
 91                 } else if((!l && u) || (l && !u)) {
 92                     if(maps[i + 1][j] == '.')
 93                         transfer(dp[now][Hash[nows + (l + u) * bit3[j]]], dp[pre][k]);
 94                     if(maps[i][j + 1] == '.')
 95                         transfer(dp[now][Hash[nows + (l + u) * bit3[j + 1]]], dp[pre][k]);
 96                 }
 97             }
 98         }
 99         swap(pre, now);
100         CC(dp[now], -1);//必须CC一下,把不合法的状态设为-1
101         for(int k = 0, s; s = status[k], s < bit3[m]; k++)
102             if(dp[pre][k] != -1)
103                 dp[now][Hash[s * 3]] = dp[pre][k];
104     }
105     int state = (1 + 2 *  bit3[m - 1]) * 3;
106     return dp[now][Hash[state]];
107 }
108 int main() {
109     int n, m;
110     preprocess();
111     while(scanf("%d %d", &n, &m) == 2 && (n + m)) {
112         CC(maps, 0);
113         REP(i, 0, n) scanf("%s", maps[i]);
114                 maps[n][0] = maps[n][m - 1] = '.';
115         LL res = DP(n, m);
116         printf("%I64d\n", res == -1 ? 0 : res);
117     }
118     return 0;
119 }

hdu 3377也是这个类型的题目,
题目要求从左上角走到右下角的路径权值最大,我们在外面构造一圈墙(构墙方法自己YY),然后用连通性DP求一个最大值即可。

需要注意的是

1.过程中取最大值,而不是累加。

2.除了左上角第一格,其余情况当遇到左位上位均是0的情况,可以不选择该点。

3.合并左位和上位分别是右括号和左括号情况依然之发生在最后一个合法位置。

4.墙权值要足够小,我给的是-INF(4e8)。防止最后不是回路,或者非法回路。

然后就依然是这类DP的经典转移方法。不符代码了

然后我们加深一下难度,用矩阵乘法加速DP的转移过程,zoj  3256就是这样提到题,首先预处理状态直接的转移,然后用矩阵加速。

 首先用枚举一层的开始状态(设为S),然后用插头的转移方式,逐个递推,知道最后一格推完位置,设太状态为T,则S->T就是一个层间转移的合法状态。

穷举之后,就可以用矩阵转移了。

注意事项:

1.矩阵有127行列,递归容易暴栈,最好迭代快速幂。

2.由于特别起点和终点都在第一列,所以我们不需要加墙,只需要特别处理计数时的合法状态。

3.模数很小,可以计算完每个格子的时候再模,不需要计算一步模一步,不然会超时的。

注意以上事项之后就一道简单插头+矩阵运算的题目了……(还是很难写的……正式比赛遇到插头需要慎重,即使只是简单插头……)

代码如下:

ZOJ 3256
  1 #include <iostream>
  2 #include <cstring>
  3 #include <map>
  4 #include <cstdio>
  5 #include <vector>
  6 #include <algorithm>
  7 #include <cassert>
  8 #define REP(i, l, n) for(int i = l;i < int(n);i++)
  9 using namespace std;
 10 const int N = 9;
 11 const int MOD = 7777777;
 12 typedef long long LL;
 13 int bit3[N] = {1};
 14 vector<int> state;
 15 map<int, int> id_table;
 16 const int maxn = 127;
 17 struct Matrix {
 18     LL A[maxn][maxn];
 19     int size;
 20     Matrix() {
 21         memset(this, 0, sizeof (*this));
 22     }
 23     void set_E() {
 24         for(int i = 0;i < size;i++) {
 25             A[i][i] = 1;
 26         }
 27     }
 28 };
 29 Matrix operator*(const Matrix& m1, const Matrix& m2) {
 30     Matrix ret;
 31     ret.size = m1.size;
 32     for (int i = 0; i < ret.size; ++i)
 33         for (int j = 0; j < ret.size; ++j) {
 34             ret.A[i][j] = 0;
 35             for (int k = 0; k < ret.size; ++k) {
 36                 ret.A[i][j] += m1.A[i][k] * m2.A[k][j];
 37             }
 38             ret.A[i][j] %= MOD;
 39         }
 40     return ret;
 41 }
 42 Matrix mypower(const Matrix& m, int n) {
 43     Matrix ret, tmp = m;
 44     ret.size = m.size;
 45     ret.set_E();
 46     while(n != 0) {
 47         if(n & 1) {
 48             ret = ret * tmp;
 49         }
 50         n >>= 1;
 51         tmp = tmp * tmp;
 52     }
 53     return ret;
 54 }
 55 Matrix A;
 56 bool check(int s) {
 57     int cnt = 0, n;
 58     while(s) {
 59         n = s % 3;
 60         if(n == 1) cnt++;
 61         if(n == 2) cnt--;
 62         if(cnt < 0) return false;
 63         s /= 3;
 64     }
 65     return (cnt == 0);
 66 }
 67 int getbit(int s, int i) {
 68     while(i-- > 0) s /= 3;
 69     return s % 3;
 70 }
 71 int n, m;
 72 void dfs(const int pre_state, int cur_state, const int step, const int n) {
 73     if(step == n) {
 74         if(cur_state < bit3[n]) {
 75             cur_state *= 3;
 76             int a = id_table[pre_state];
 77             map<int, int>::iterator iter = id_table.find(cur_state);
 78             if(iter != id_table.end()) {
 79                 A.A[a][iter->second]++;
 80             }
 81         }
 82         return;
 83     }
 84     int l = getbit(cur_state, step), u = getbit(cur_state, step + 1);
 85     int nows = cur_state - l * bit3[step] - u * bit3[step + 1];
 86     if(l == 0 && u == 0) { //当向下向右都可以走的时候建立两个插头
 87         int nxt = nows + bit3[step] + 2 * bit3[step + 1];
 88         dfs(pre_state, nxt, step + 1, n);
 89     } else if(l == 1 && u == 1) { // 合并2个左插头,整理相应的右插头
 90         int cnt = 0;
 91         for(int b = step + 2; b <= m; b++) {
 92             int tmp = getbit(nows, b);
 93             if(tmp == 2) cnt--;
 94             if(tmp == 1) cnt++;
 95             if(cnt == -1) {
 96                 dfs(pre_state, nows - bit3[b], step + 1, n);
 97                 break;
 98             }
 99         }
100     } else if(l == 2 && u == 2) { // 合并2个右插头,整理相应的左插头
101         int cnt = 0;
102         for(int b = step - 1;b >= 0;b--) {
103             int tmp = getbit(nows, b);
104             if(tmp == 1) cnt++;
105             if(tmp == 2) cnt--;
106             if(cnt == 1) {
107                 dfs(pre_state, nows + bit3[b], step + 1, n);
108                 break;
109             }
110         }
111     } else if(l == 2 && u == 1) { //合并)(,什么都不用整理
112         dfs(pre_state, nows, step + 1, n);
113     } else if((!l && u) || (l && !u)) {
114         dfs(pre_state, nows + (l + u) * bit3[step], step + 1, n);
115         dfs(pre_state, nows + (l + u) * bit3[step + 1], step + 1, n);
116     }
117 }
118 int main() {
119     REP(i, 1, N) bit3[i] = bit3[i - 1] * 3;
120     REP(i, 0, bit3[N - 1]) {
121         if(i % 3 == 0 && check(i)) {
122             id_table[i] = state.size();
123             state.push_back(i);
124         }
125     }
126     while(cin >> n >> m) {
127         A = Matrix();
128         int _size = 0;
129         for(int i = 0;i < int(state.size()) && state[i] < bit3[n + 1];i++) {
130             dfs(state[i], state[i], 0, n);
131             _size++;
132         }
133         A.size = _size;
134         Matrix B = mypower(A, m);
135         int b = id_table[(1 + 2 * bit3[n - 1]) * 3];
136         LL res = B.A[0][b];
137         if(res == 0) {
138             cout << "Impossible" << endl;
139         } else {
140             cout << res << endl;
141         }
142     }
143     return 0;
144 }

 

转载于:https://www.cnblogs.com/ronaflx/archive/2012/08/13/2635877.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值