1. 程式人生 > >Codeforces Round #538 (Div. 2) D. Flood Fill 【區間dp || LPS (最長回文序列)】

Codeforces Round #538 (Div. 2) D. Flood Fill 【區間dp || LPS (最長回文序列)】

最長 tle res connected byte ima title aps 例如

任意門:http://codeforces.com/contest/1114/problem/D

D. Flood Fill time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output

You are given a line of nn colored squares in a row, numbered from 11 to nn from left to right. The ii-th square initially has the color

cici.

Let‘s say, that two squares ii and jj belong to the same connected component if ci=cjci=cj, and ci=ckci=ck for all kk satisfying i<k<ji<k<j. In other words, all squares on the segment from ii to jj should have the same color.

For example, the line [3,3,3][3,3,3] has 11 connected component, while the line

[5,2,4,4][5,2,4,4] has 33 connected components.

The game "flood fill" is played on the given line as follows:

  • At the start of the game you pick any starting square (this is not counted as a turn).
  • Then, in each game turn, change the color of the connected component containing the starting square to any other color.

Find the minimum number of turns needed for the entire line to be changed into a single color.

Input

The first line contains a single integer nn (1n50001≤n≤5000) — the number of squares.

The second line contains integers c1,c2,,cnc1,c2,…,cn (1ci50001≤ci≤5000) — the initial colors of the squares.

Output

Print a single integer — the minimum number of the turns needed.

Examples input Copy
4
5 2 2 1
output Copy
2
input Copy
8
4 5 2 2 1 3 5 5
output Copy
4
input Copy
1
4
output Copy
0
Note

In the first example, a possible way to achieve an optimal answer is to pick square with index 22 as the starting square and then play as follows:

  • [5,2,2,1][5,2,2,1]
  • [5,5,5,1][5,5,5,1]
  • [1,1,1,1][1,1,1,1]

In the second example, a possible way to achieve an optimal answer is to pick square with index 55 as the starting square and then perform recoloring into colors 2,3,5,42,3,5,4 in that order.

In the third example, the line already consists of one color only.

題意概括:

給一個序列不同數字代表不同顏色,相同顏色為一連通塊,每次操作可以把起點所在的連通塊的顏色全部置為任一種顏色。

求將色塊的顏色變成全部一樣的最少操作次數。

解題思路:

因為顏色相同可以看作一個連通塊,所以預處理我們可以先壓縮序列。

一、區間DP:

狀態:dp[ L ][ R ][ 0 ] 表示將 [ L, R ] 區間的顏色變成 Color[ L ] 所需的最少次數

   dp[ L ][ R ][ 1 ] 表示將 [ L, R ] 區間的顏色變成 Color[ R ] 所需的最少次數

轉移方程:

   IF: Color[ L + 1] == Color [ L ] dp[ L ][ R ][ 0 ] = min( dp[ L ][ R ][ 0 ], dp[ L + 1 ][ R ][ 0 ] );

   IF: Color[ L + 1] != Color [ L ] dp[ L ][ R ][ 0 ] = min( dp[ L ][ R ][ 0 ], dp[ L + 1 ][ R ][ 0 ] + 1);

   IF: Color[ R -1] == Color [ R ] dp[ L ][ R ][ 1 ] = min( dp[ L ][ R ][ 1 ], dp[ L ][ R - 1 ][ 1 ] );

   IF: Color[ R -1] != Color [ R ] dp[ L ][ R ][ 1 ] = min( dp[ L ][ R ][ 1 ], dp[ L ][ R - 1 ][ 1 ] + 1);

AC code( 218 ms):

技術分享圖片
 1 /// 區間dp
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cstring>
 6 #include<vector>
 7 #include<queue>
 8 #include<cmath>
 9 #include<set>
10 #define INF 0x3f3f3f3f
11 #define LL long long
12 using namespace std;
13 const int MAXN = 5e3+10;
14 int dp[MAXN][MAXN][2];
15 int num[MAXN];
16 bool vis[MAXN];
17 
18 int main()
19 {
20     int N = 0, x = -1, cnt = 0;
21     scanf("%d", &cnt);
22     for(int i = 1; i <= cnt; i++){
23         scanf("%d", &x);
24         if(x != num[N]) num[++N] = x;
25     }
26 
27     for(int i = 1; i <= N; i++)
28         for(int j = 1; j <= N; j++)
29             dp[i][j][0] = dp[i][j][1] = (i == j?0:INF);
30 
31     for(int r = 1; r <= N; r++)
32     for(int l = r; l > 0; l--)
33     for(int it = 0; it < 2; it++){
34         int c = (it==0?num[l]:num[r]);
35         if(l > 1) dp[l-1][r][0] = min(dp[l-1][r][0], dp[l][r][it]+int(c != num[l-1]));
36         if(r < N) dp[l][r+1][1] = min(dp[l][r+1][1], dp[l][r][it]+int(c != num[r+1]));
37     }
38 
39     printf("%d\n", min(dp[1][N][0], dp[1][N][1]));
40 
41     return 0;
42 }
View Code

二、LPS (最長回文序列長度)

試想一下,我們需要變換的點其實是那一些 不在最長回文子序列裏的點。

例如: 4 1 3 7 6 3 1 5

   最長回文子序列是 1 3 3 1,為了結果最優,我們肯定優先從 7 6 入手,而回文子序列只需要操作一半即可(因為對稱),剩下就是處理 4 5。

那麽問題就轉換為了求LPS:

兩種求法:

①直接dp:

AC code(109 ms):

技術分享圖片
 1 /// LCS
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cstring>
 6 #include<vector>
 7 #include<queue>
 8 #include<cmath>
 9 #include<set>
10 #define INF 0x3f3f3f3f
11 #define LL long long
12 using namespace std;
13 const int MAXN = 5e3+10;
14 int num[MAXN];
15 int dp[MAXN][MAXN];
16 
17 int main()
18 {
19     int N = 0, cnt, x;
20     scanf("%d", &cnt);
21     for(int i = 1; i <= cnt; i++){
22         scanf("%d", &x);
23         if(x != num[N]) num[++N] = x;
24     }
25     for(int i = 0; i <= N; i++) dp[i][i] = 1;
26 
27     for(int i = 1; i < N; i++)
28     for(int j = 1; j+i <= N; j++){
29         if(num[j] == num[j+i]) dp[j][j+i] = dp[j+1][j+i-1] + 2;
30         else dp[j][i+j] = max(dp[j+1][j+i], dp[j][j+i-1]);
31     }
32 
33     int ans = N-(dp[1][N]+1)/2;
34     printf("%d\n", ans);
35 
36     return 0;
37 
38 }
View Code

②反向復制原序列,通過原序列求 LCS (最長公共序列)

AC code(78 ms):

/// LCS
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<set>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int MAXN = 5e3+10;
int num[MAXN], b[MAXN];
int dp[MAXN][MAXN];

int main()
{
    int N = 0, cnt, x;
    scanf("%d", &cnt);
    for(int i = 1; i <= cnt; i++){
        scanf("%d", &x);
        if(x != num[N]) num[++N] = x;
    }
    int E = N;
    for(int i = 1; i <= N; i++){
            b[E--] = num[i];
    }

    for(int i = 1; i <= N; i++)
    for(int j = 1; j <= N; j++){
        if(num[i] == b[j]) dp[i][j] = dp[i-1][j-1]+1;
        else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
    }
    int ans = N-(dp[N][N]+1)/2;

    printf("%d\n", ans);

    return 0;

}

Codeforces Round #538 (Div. 2) D. Flood Fill 【區間dp || LPS (最長回文序列)】