D. Flood Fill 区间DP 或lcs匹配
题意 给定一串数字 相同的连续的数字可以同时 转换成一个相同数字 问最小几次可以全部转换成一个相同的数字
法1:区间dp dp[l][r][0/1] 0表示l r区间转化成和最左边相同需要多少次 1表示转化成和最右边相同 区间dp即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;i++) 3 #define MS(arr,arr_value) memset(arr,arr_value,sizeof(arr)) 4 #define F first 5 #define S second 6 #define pii pair<int ,int > 7 #define mkp make_pair 8 #define pb push_back 9 using namespace std; 10 typedef long long ll; 11 const int maxn=5e3+4; 12 int c[maxn],a[maxn],b[maxn]; 13 int dp[maxn][maxn][2]; 14 int main(){ 15 int n; 16 MS(dp,0x3f3f3f3f); 17 scanf("%d",&n); 18 FOR(i,1,n)scanf("%d",&c[i]); 19 int cnt=0; 20 int p=1; 21 while(p<=n){ 22 if(c[p]!=c[p-1]||p==1)a[++cnt]=c[p]; 23 p++; 24 } 25 for(int i=1;i<=cnt;i++){ 26 dp[i][i][0]=dp[i][i][1]=0; 27 } 28 for(int len=1;len<=cnt;len++){ 29 for(int l=1,r=len+l;r<=cnt;r++,l++){ 30 dp[l][r][0]=min(dp[l][r][0],dp[l+1][r][0]+1); 31 32 dp[l][r][0]=min(dp[l][r][0],dp[l+1][r][1]+!(a[r]==a[l])); 33 34 dp[l][r][1]=min(dp[l][r][1],dp[l][r-1][1]+1); 35 36 dp[l][r][1]=min(dp[l][r][1],dp[l][r-1][0]+!(a[l]==a[r])); 37 } 38 } 39 cout<<min(dp[1][cnt][0],dp[1][cnt][1])<<endl; 40 return 0; 41 }
法2:LCS 从题目可以看出 如果转换成n个互相不连续的数字之后,如果所有数字都不相同则需要转换n-1次才能转换成一种答案
如果存在 例如1 2 3 4 2 5 有区间[2,5] 这时如果先转化2 5 之间的数字 即可少转化一次 那么问题就转换成 求最大不相交的这种区间有多少个(相交不行,因为相交 中间夹的那个点就被更改了)
而求最大相交的区间有多少个 就是把原序列翻转后的序列和原序列求lcs 因为lcs配对的过程 在原序列中的i 和翻转序列的j 就相当于 在原序列左右两边配对 所以不会相交 而因为是翻转的序列 所以会求两遍
所以要/2 并且有一个区间的左右是重合的也就退化成了一个点,不能算 (这里在除以2的时候已经被消气了)减1 是因为 没有区间的时候是n-1的,每多一个区间都可以-1 这样答案就是总共的点数n-1-floor(lcs(s)/2);
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;i++) 3 #define MS(arr,arr_value) memset(arr,arr_value,sizeof(arr)) 4 #define F first 5 #define S second 6 #define pii pair<int ,int > 7 #define mkp make_pair 8 #define pb push_back 9 using namespace std; 10 typedef long long ll; 11 const int maxn=5e3+4; 12 int c[maxn],a[maxn],b[maxn]; 13 int dp[maxn][maxn]; 14 int main(){ 15 int n; 16 scanf("%d",&n); 17 FOR(i,1,n)scanf("%d",&c[i]); 18 int cnt=0; 19 int p=1; 20 while(p<=n){ 21 if(c[p]!=c[p-1]||p==1)a[++cnt]=c[p]; 22 p++; 23 } 24 memcpy(b,a,sizeof(a)); 25 reverse(b+1,b+cnt+1); 26 // for(int i=1;i<=cnt;i++)cout<<b[i]<<" "; 27 // puts(""); 28 for(int i=1;i<=cnt;i++){ 29 for(int j=1;j<=cnt;j++) 30 { 31 if(a[i]==b[j]){ 32 dp[i][j]=dp[i-1][j-1]+1; 33 } 34 else dp[i][j]=max(dp[i-1][j],dp[i][j-1]); 35 } 36 } 37 printf("%d\n",cnt-1-dp[cnt][cnt]/2); 38 return 0; 39 }
两种方法参考:https://www.cnblogs.com/pkgunboat/p/10361375.html
区间dp 参考:https://blog.csdn.net/moon_sky1999/article/details/87171499