loj 6037 「雅礼集训 2017 Day4」猜数列 - 动态规划

题目传送门

  传送门

题目大意

  有一个位置数列,给定$n$条线索,每条线索从某一个位置开始,一直向左或者向右走,每遇到一个还没有在线索中出现的数就将它加入线索,问最小的可能的数列长度。

  依次从左到右考虑每一位上填的数。

  用$f_{L, a, R, b, S}$表示正在满足向右走的线索是$L$,前$a$个字符已经满足,正在满足向左走的线索是$R$,前$b$个字符还没有满足,还未被考虑的线索集合是$S$。

  主要有两种转移:

  • 填下一个字符
    • 如果两个线索下一个要填的字符相同,那么直接填
    • 如果不同则还需判断一下是否会使得另一线索不满足条件。
  • 更换线索
    • 向右走的线索是一堆类似于后缀的东西,向左走的线索是一堆类似于前缀的东西
    • 能不能在某个串的某个位置处更换某个串可以预处理出来

  loj上加了一堆常数优化卡到rk 1,估计很快就被超了。

  记得Doggu指着Claris这道题的非记忆化搜索写法给我说以后见着Claris记着%。

Code

  1 /**
  2  * loj
  3  * Problem#6037
  4  * Accepted
  5  * Time: 1224ms
  6  * Memory: 25208k
  7  */
  8 #include <iostream>
  9 #include <cstdlib>
 10 #include <cstdio>
 11 #include <set>
 12 using namespace std;
 13 typedef bool boolean;
 14 
 15 const int N = 11;
 16 const int Lim = 1 << 10;
 17 
 18 #define last_one(__x) (__builtin_ffs(__x) - 1)
 19 
 20 int n;
 21 int len[N];
 22 int s[N][N];
 23 int exi[N][N];
 24 int can[N][N];    // L: forward, R: backward
 25 int usable[N][N];
 26 int f[N][N][N][N][1024];
 27 
 28 inline void init() {
 29     scanf("%d", &n);
 30     set<int> ss;
 31     for (int i = 0, x; i < n; i++) {
 32         int l = 0, hash_val = 0;
 33         while (~scanf("%d", &x) && x) {
 34             s[i][l++] = x;
 35             hash_val = hash_val * 10 + x;
 36         }
 37         len[i] = l, s[i][l] = 0;
 38         if (ss.count(hash_val))
 39             n--, i--;
 40         else
 41             ss.insert(hash_val);
 42     }
 43 
 44     for (int i = 0; i < n; i++)
 45         for (int j = 0; j < len[i]; j++)
 46             exi[i][j + 1] = exi[i][j] | (1 << s[i][j]);
 47 }
 48 
 49 // start at pos
 50 boolean check(int a, int pos, int b) {
 51     int *pa = s[a] + pos, *pb = s[b];
 52     while (*pa || *pb) {
 53         if (*pa == *pb)
 54             pa++, pb++;
 55         else if ((1 << *pb) & exi[a][pa - s[a]])
 56             pb++;
 57         else
 58             return false;
 59     }
 60     return true;
 61 }
 62 
 63 void upd(int& a, int b) {
 64     if (a > b)
 65         a = b;
 66 }
 67 
 68 // considering s[L][pl], s[R][pr - 1], S remained
 69 int dp(int L, int pl, int R, int pr, int S) {
 70     if (!S && pl == len[L] && !pr)
 71         return 0;
 72     int &rt = f[L][pl][R][pr][S];
 73     if (rt)
 74         return rt;
 75     rt = Lim;
 76     
 77     for (int T = S & can[L][pl], i = last_one(T); T; T -= (T & (-T)), i = last_one(T))
 78         upd(rt, dp(i, 0, R, pr, S ^ (1 << i)));
 79     if (!pr) {
 80         for (int i = 0; i < n && (S >> i); i++)
 81             if ((S >> i) & 1)
 82 //                for (int j = 0; j <= len[i]; j++)
 83 //                    if ((can[i][j] >> R) & 1)
 84 //                        upd(rt, dp(L, pl, i, j, S ^ (1 << i)));
 85                 for (int T = usable[R][i], j = last_one(T); T; T -= (T & (-T)), j = last_one(T))
 86                     upd(rt, dp(L, pl, i, j, S ^ (1 << i)));
 87     }
 88     
 89     if (pl < len[L] || pr) {
 90         int vl = s[L][pl], vr = ((pr) ? (s[R][pr - 1]) : (0));
 91         if (vl == vr)
 92             upd(rt, dp(L, pl + 1, R, pr - 1, S) + 1);
 93 //        if (pl < len[L] && _exi[R][pr] & (1 << vl))
 94         if (pl < len[L] && exi[R][pr] & (1 << vl))
 95             upd(rt, dp(L, pl + 1, R, pr, S) + 1);
 96         if (pr && exi[L][pl] & (1 << vr))
 97             upd(rt, dp(L, pl, R, pr - 1, S) + 1);
 98     }
 99 //    cerr << L << " " << pl << " " << R << " " << pr << " " << S  << " " << rt << '\n';
100     return rt;
101 }
102 
103 inline void solve() {
104     // forward
105     for (int idx = 0; idx < n; idx++) {
106         for (int pos = 0; pos <= len[idx]; pos++) {
107             for (int ano = 0; ano < n; ano++) {
108                 if (ano ^ idx)
109                     can[idx][pos] |= check(idx, pos, ano) << ano;
110             }
111 //            cerr << can[idx][pos] << ' ';
112         }
113     }
114     for (int i = 0; i < n; i++) {
115         for (int j = 0; j < n; j++) {
116             if (i ^ j) {
117                 for (int pos = 0; pos <= len[j]; pos++)
118                     if ((can[j][pos] >> i) & 1)
119                         usable[i][j] |= (1 << pos);
120             }
121         }
122     }
123     len[n] = 0;
124     for (int i = 0; i < N; i++) {
125         exi[n][i] = 2046; //_exi[n][i] = 2046;
126         can[n][i] = 2047;
127     }
128     int all = (1 << n) - 1, ans = Lim;
129 //    ans = dp(n, 0, 1, len[1], all ^ 2);
130     for (int i = 0; i < n; i++) {
131         upd(ans, dp(n, 0, i, len[i], all ^ (1 << i)));
132     }
133     if (ans == Lim)
134         puts("-1");
135     else
136         printf("%d\n", ans);
137 }
138 
139 int main() {
140     init();
141     solve();
142     return 0;
143 }
posted @ 2018-12-15 21:53 阿波罗2003 阅读(...) 评论(...) 编辑 收藏