http://codeforces.com/contest/1114/problem/D

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 (1≤n≤50001≤n≤5000 ) — the number of squares.

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

Output

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

 

题意:连续的几个颜色相同的格子称为一个连通块。选一个点为起点,每个操作是把所在连通块变一个颜色,求把整个区间染成同色需要的最少操作数。(注意,每次只能改变所在连通块的颜色,不能任选连通块,除了最开始时)

解法:首先,不能用线性dp,举个例子12321,应该先3变为2,再2变为1,从左到右的顺序是不行的。(我开始设f(i,j):前i个格子全涂j色的最少操作数,错了)

应该是区间dp。

首先要明白,对于区间[L,R],最优的方案要么是全变成L处的颜色,要么全变成R处的颜色

因为可以看作是先选一个格子为起点,然后不断地将当前所在联通块与相邻格子合并,合并后一定是相邻格子的颜色才最优

那么,设f(i,j,0/1)表示区间[i,j]变为i/j处的颜色的最少操作次数

f(i,j,0)由f(i+1,j,0/1)转移来,f(i,j,1)由f(i,j-1,0/1)转移来,转移附加个颜色是否相同就行了,见代码。

还有一种方法是:先把初始颜色序列去重,设去重后长度为n,然后找最长回文子序列len,答案就是n-ceil(len/2)。

因为对于一个回文子序列,只需操作floor(len/2)次,非回文序列长度为n必须两两合并共n-1次,因为起点可以任选,所以选最长回文子序列的中点作为起点,共操作n-ceil(len/2)次。

找最长回文子序列的方法是原序列和原序列的反转求LCS的长度。证明在此:https://stackoverflow.com/questions/54347339/longest-palindromic-subsequence-dp-solution

第一种方法:

#include<bits/stdc++.h>
using namespace std;
#define ll long long

int n,c[5005],f[5005][5005][2];

int main()
{
//	freopen("input.in","r",stdin);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>c[i];
	for(int k=2;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			int j=i+k-1;
			if(j>n)break;
			f[i][j][0]=min(f[i+1][j][0]+(c[i]!=c[i+1]),f[i+1][j][1]+(c[i]!=c[j]));
			f[i][j][1]=min(f[i][j-1][0]+(c[i]!=c[j]),f[i][j-1][1]+(c[j-1]!=c[j]));
		}
	}
	cout<<min(f[1][n][0],f[1][n][1])<<endl;
	return 0;
}

方法2的官方题解: