[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 }
View Code

 

posted @ 2021-04-27 22:39  _onglu  阅读(58)  评论(0编辑  收藏  举报