Cities 题解(神仙区间dp)

题目链接

题目大意

给你长度为\(n(n\leq 5000)\)的数组\(a(1\le a[i]\le n)\)

你每次可以选择一段连续且相同的元素使得它整体变为一个其他值

求你使得所有元素相等的最少操作次数

每个元素最多出现\(15\)

题目思路

首先可以进行缩点操作

如果最开始相邻的一段元素相等,那么这一段可以等价为一个点

则可以变为有\(m\)个相邻元素不相等的点

假如这\(m\)个点都不同,那么答案就是\(m-1\)

而如果中间出现了有两个相同的点\(l,r\),那么最优的情况肯定是把\([l+1,r-1]\)中的元素全部变为\(a[r]\)

然后再整体操作\([l,r]\)这样显然操作的次数更少,则区间在最短的操作次数下,一定能修改成端点的值

则可以设\(dp[l][r]\)为把这个区间变为\(a[r]\)的最少操作次数

而转移的话如果\([l,r-1]\)中没有元素等于\(a[r]\),那么答案显然就是\(dp[l][r-1]+1\)

就是先把区间\([l,r-1]\)的元素全部变为\(a[r-1]\)然后再全部变为\(a[r]\)

转移有可能比这个更优,因为\([l,r-1]\)中可以出现\(a[r]\)元素

那么就可以用这个进行中间转移

时间复杂度\(O(n^2\times 15)\)

这个有点难想,建议多想几遍

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=5e3+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,k;
int a[maxn];
int dp[maxn][maxn];
signed main(){
    int _;scanf("%d",&_);
    while(_--){
        vector<int> vec[maxn];
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            vec[a[i]].push_back(i);
        }
        memset(dp,0x3f,sizeof(dp));
        for(int i=1;i<=n;i++) dp[i][i]=0;
        for(int l=n;l>=1;l--){
            for(int r=l+1;r<=n;r++){
                dp[l][r]=dp[l][r-1]+1;
                for(int i=0;i<vec[a[r]].size();i++){
                    int x=vec[a[r]][i];
                    if(l<=x&&x<r){
                        dp[l][r]=min(dp[l][r],dp[l][x]+dp[x+1][r]);
                    }
                }
            }
        }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}


posted @ 2021-04-09 09:40  hunxuewangzi  阅读(133)  评论(0编辑  收藏  举报