[URAL1519]Formula 1 Bzoj1814

此题作为陈丹琦《基于连通性状态压缩的动态规划问题》的例题1,主要的思想是扫描线、插头和括号匹配。具体的思路借鉴陈丹琦《基于连通性状态压缩的动态规划问题》

扫描线采用三进制存储,第0m-1位对应扫描线0m-1位置上的上插头,第m位对应左插头。虽然使用四进制可以使用位运算,但四进制状态数过多,达到4^13,必须使用hash,否则空间难以承受,这样一来四进制倒不一定比三进制快。使用一个队列记录下上一次状态更新过哪些节点,这一次的状态从当中更新即可,这样省去了枚举上一次的状态,节省了不少时间。另外,对于括号的匹配,虽然预处理之后DP只需要O(1)即可完成转移,但会大大增加代码长度和内存使用,不如到DP的时候再进行扫描,反正也只需要O(m)的时间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <iostream>
using namespace std;

struct QueueType
{
  int opt;
  long long sum;
} q[2][13944];
char map[13][13];
int n, m, j, now, last, power[13], pos[2][1594324], tail[2];

inline int Get(const int data, const int index)
{
  return data/power[index]%3;
}
inline int Set(int data, const int index, const int value)
{
  data = data+(-data/power[index]%3+value)*power[index];
  return data;
}
int ChangeRightToLeft(const int opt, int i)
{
  int stack = 0;
  for (; i < m; ++i)
  {
    int bit = Get(opt, i);
    if (bit == 1)
      stack += 1;
    else if (bit == 2)
      stack -= 1;
    if (!stack)
      return Set(opt, i, 1);
  }
  return 0x7FFFFFFF;
}
int ChangeLeftToRight(const int opt, int i)
{
  int stack = 0;
  for (; i >= 0; --i)
  {
    int bit = Get(opt, i);
    if (bit == 2)
      stack += 1;
    else if (bit == 1)
      stack -= 1;
    if (!stack)
      return Set(opt, i, 2);
  }
  return 0x7FFFFFFF;
}
void EnQueue(const int opt, const long long sum)
{
  if (j == m-1 && Get(opt, m) != 0)
    return;
  if (pos[now][opt])
    q[now][pos[now][opt]].sum += sum;
  else
  {
    pos[now][opt] = ++tail[now];
    q[now][tail[now]].opt = opt;
    q[now][tail[now]].sum = sum;
  }
}
int main()
{
  power[0] = 1;
  for (int i = 1; i <= 13; ++i)
    power[i] = power[i-1]*3;
  ios::sync_with_stdio(false);
  cin >> n >> m;
  for (int i = 0; i != n; ++i)
    for (int j = 0; j != m; ++j)
    {
      cin >> map[i][j];
      if (map[i][j] == '.')
        last = i*m+j;
    }

  EnQueue(0, 1);
  now ^= 1;
  for (int i = 0; i != n; ++i)
    for (j = 0; j != m; ++j)
    {
      for (int head = 1; head <= tail[now^1]; ++head)
      {
        int opt = q[now^1][head].opt;
        long long sum = q[now^1][head].sum;
        int up = Get(opt, j), left = Get(opt, m);
        bool crash = map[i][j] == '*';
        if (up == 0 && left == 0) //Case 1
        {
          if (!crash)
            opt = Set(Set(opt, j, 1), m, 2);
          EnQueue(opt, sum);
        }
        else if (up == 2 && left == 1) //Case 2.3
        {
          if (i*m+j == last)
            EnQueue(Set(Set(opt, j, 0), m, 0), sum);
        }
        else if (crash)
          continue;
        else if (up == 0 || left == 0) //Case 3
        {
          EnQueue(Set(Set(opt, j, up+left), m, 0), sum);
          EnQueue(Set(Set(opt, m, up+left), j, 0), sum);
        }
        else if (up == 1 && left == 1) //Case 2.1
        {
          opt = ChangeRightToLeft(opt, j);
          EnQueue(Set(Set(opt, j, 0), m, 0), sum);
        }
        else if (up == 2 && left == 2) //Case 2.1
        {
          opt = ChangeLeftToRight(opt, j);
          EnQueue(Set(Set(opt, j, 0), m, 0), sum);
        }
        else //if (up == 1 && left == 2) //Case 2.2
        {
          EnQueue(Set(Set(opt, j, 0), m, 0), sum);
        }
      }
      now ^= 1;
      for (int head = 1; head <= tail[now]; ++head)
        pos[now][q[now][head].opt] = 0;
      tail[now] = 0;
    }
  cout << q[now^1][pos[now^1][0]].sum << endl;
}

Comments