依次从左到右考虑每一位上填的数。
用$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 << ' '; 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 ", ans); 137 } 138 139 int main() { 140 init(); 141 solve(); 142 return 0; 143 }