[BZOJ] 1032 [JSOI2007]祖码
似乎这道题是一道假题,因为std没考虑完全,导致有很多hack数据可以hack。。
各种题解也都被各种hack,似乎只能暴搜写??
难怪我30min就切掉了,算了假算法就假算法吧,来讲讲我的假做法。
BZOJ上有另一道Zuma2,那道题的做法是真的,那道题在我之前的博文里面有,这里不提了。
错误做法就是区间dp((
设$g[i][j]$表示消除$[i,j]$的最小代价。
有两种方式可以消除:
1.分成两段,左右两边分别消除。
2.消除中间的一堆,然后剩下左右两边的合并再消除。
其实还有一种就是可以把跟两边相同的全部合并消除,不过那样子就有很多很多情况要讨论了,因为三个东西碰到一起就会自动消除,所以在掏空中间的时候会遇到很多问题,而std估计没考虑这种情况,所以这是个假题。
所以状态转移方程就很好写了。
$g[i][j] = min{g[i][k - 1] + g[k][j], g[l][r] + cost_{combine}}$。
每次判断两边多少个同色然后合并就好了。。
1 #include <bits/stdc++.h> 2 #define Mid ((l + r) / 2) 3 #define lson (rt << 1) 4 #define rson (rt << 1 | 1) 5 using namespace std; 6 int read() { 7 char c; int num, f = 1; 8 while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; 9 while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; 10 return f * num; 11 } 12 const int N = 509; 13 int n, a[N], g[N][N]; 14 signed main() 15 { 16 memset(g, 0x3f, sizeof(g)); 17 n = read(); 18 for(int i = 1; i <= n; i++) a[i] = read(); 19 for(int i = 1; i <= n; i++) g[i][i] = 2; 20 for(int len = 2; len <= n; len++) { 21 for(int i = 1; i + len - 1 <= n; i++) { 22 int j = i + len - 1; 23 for(int k = i + 1; k <= j; k++) 24 g[i][j] = min(g[i][j], g[i][k - 1] + g[k][j]); 25 int l = i, r = j; 26 if(a[l] != a[r]) continue; 27 while(l < j && a[l + 1] == a[l]) l++; 28 while(r > i && a[r - 1] == a[r]) r--; 29 if(l == j) g[i][j] = min(g[i][j], (j - i + 1) >= 2 ? 1 : (3 - (j - i + 1))); 30 else { 31 if((l - i + 1) + (j - r + 1) >= 3) g[i][j] = min(g[i][j], g[l + 1][r - 1]); 32 else g[i][j] = min(g[i][j], g[l + 1][r - 1] + 3 - ((l - i + 1) + (j - r + 1))); 33 } 34 } 35 } 36 printf("%d\n", g[1][n] == 3 ? 2 : g[1][n]); 37 return 0; 38 }