C-Cities(区间DP)
题意:
给n个数。每次操作可以将连续的一段相同(可以只有一个数)的数变为任意数。
求最少多少次操作可以使所有数都相同。
思路:
区间DP,直接区间DP会超时。
需要进行优化,对于区间[L,R],可以找到与a[r]相同的数,然后把区间分为两半。
如果a[l]==a[r],则dp[L][R]–;
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[5005],pre[5005];
void solve(){
int dp[n+1][n+1];
for(int i=1;i<=n;i++)
dp[i][i]=0;
for(int len=2;len<=n;len++){
for(int l=1;l<=n-len+1;l++){
int r=l+len-1;
dp[l][r]=dp[l][r-1]+1;
for(int j=pre[r];j>l;j=pre[j]){
dp[l][r]=min(dp[l][r],dp[l][j-1]+dp[j][r]+1);
}
if(a[l]==a[r])dp[l][r]--;
}
}
cout<<dp[1][n]<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n;
int idx[n+1];
memset(idx,0,sizeof(idx));
for(int i=1;i<=n;i++){
cin>>a[i];
pre[i]=idx[a[i]];
idx[a[i]]=i;
}
solve();
}
}
记忆化搜索:
#include<bits/stdc++.h>
using namespace std;
int n,a[5005];
int dp[5005][5005];
int pre[5005];
int f(int l,int r){
if(dp[l][r]!=-1)
return dp[l][r];
int ans=f(l,r-1)+1;
for(int i=pre[r];i>l;i=pre[i]){
ans=min(ans,f(l,i-1)+f(i,r)+1);
}
if(a[l]==a[r])
ans--;
return dp[l][r]=ans;
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j)
dp[i][i]=0;
else
dp[i][j]=-1;
}
}
int idx[n+1];
memset(idx,0,sizeof(idx));
for(int i=1;i<=n;i++){
cin>>a[i];
pre[i]=idx[a[i]];
idx[a[i]]=i;
}
cout<<f(1,n)<<endl;
}
}