Description:
给出一个数列,先定义 component:就是数列中某一相连的数字相同的部分。
现在是一个游戏,初始时可以选择其中的一个数作为“中心”,然后每次操作可以改变包含该"中心"的一个component改变大小,问最少要操作多少次才能把所有的数都变成一样大小。
Input:
n
a[i] for(1<=i<=n)
Output:
answer
Analysis:
要用动态规划还是比较明显的:
这里一个重要的observation是,不论怎么搞,最终整个数列一定要么都和初始时左端的数相同,要么都和初始时右端的数相同。
设dp[L][R][dir] 为已经使得L到R内数字相同且 当dir=0时,数字都是初始时最左边的数,dir=1时,都是初始时最右边的数,
然后就好写了。
学习到的:这种中间到两边dp的递推顺序, 看别人用c++17写的代码有
int n=input<int>(); //这就直接输入了n
vector<int> a(n);
input_seq(a.begin(),a.end()) //这就直接输入了整个数列orz
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<sstream>
#include<cmath>
#include<iterator>
#include<bitset>
#include<stdio.h>
#include<unordered_set>
#include<ctime>
#include<cstring>
using namespace std;
#define _for(i,a,b) for(int i=(a);i<(b);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
typedef long long LL;
const int INF = 1 << 30;
const int maxn = 5005;
const int MOD = 1e9+7;
const double eps = 1e-6;
int n;
int a[maxn],dp[maxn][maxn][2];
int main()
{
//freopen("C:\\Users\\admin\\Desktop\\in.txt", "r", stdin);
//freopen("C:\\Users\\admin\\Desktop\\out.txt", "w", stdout);
while (scanf("%d", &n) == 1) {
_rep(i, 1, n) scanf("%d", &a[i]);
_rep(i,1,n)
_rep(j, 1, n) {
dp[i][j][0] = dp[i][j][1] = (i == j ? 0 : 0x3f3f3f3f);
}
for(int j=1;j<=n;++j)
for (int i = j; i >= 1; --i)
for(int k=0;k<=1;++k){
int c = (k == 0 ? a[i] : a[j]);
if (i-1>=1) dp[i - 1][j][0] = min(dp[i - 1][j][0], dp[i][j][k]+(c!=a[i-1]));
if (j + 1 <= n) dp[i][j + 1][1] = min(dp[i][j + 1][1], dp[i][j][k] + (c != a[j + 1]));
}
cout << min(dp[1][n][0], dp[1][n][1]) << endl;
}
return 0;
}