Flood Fill
【题解】:
题意是https://www.chiark.greenend.org.uk/\%7Esgtatham/puzzles/js/flood.html
根据上面的游戏进行改造的,大家需要玩一下游戏你就知道题目的意思是什么了。
题意是,给了一个5e3长度的一列数,然后每一个不同的数字定义为不同的颜色,然后根据颜色左右延伸,也相当于越少步数涂色,把整一列数字变成同一个数字,问最少需要多少步,可以从任意一个位置开始。
官方题解给出了两种做法,第一种是区间DP,根据dp[L][R][dir],
dp[L][R][dir]->从L到R区间内涂上同颜色的最少步数,然后进行dir=0,向左延伸,dir=1,向右延伸。
所以得到的推导式子为:
dp[L - 1][R][0]=min{ dp[L-1][R][0] ,dp[L][R][dir]+a[L-1]!=a[L] }
dp[L][R+1] [1]=min{ dp[L][R+1][0] ,dp[L][R][dir]+a[R+1]!=a[R] }
两个for循环进行更新,至于如何更新,那么就是右端点从左往右,左端点从右往左,不断更新。
贴上代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10;
const int inf=0x3f3f3f3f;
int dp[N][N][2];
int main()
{
int a[N],n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
dp[i][j][0]=dp[i][j][1]=(i==j?0:inf);
for(int r=0;r<=n;r++)
for(int l=r;l>=0;l--)
for(int it=0;it<2;it++){
int c=!it?a[l]:a[r];
if(l){
dp[l-1][r][0]=min(dp[l-1][r][0],dp[l][r][it]+(c!=a[l-1]));
}
if(r+1!=n){
dp[l][r+1][1]=min(dp[l][r+1][1],dp[l][r][it]+(c!=a[r+1]));
}
}
int ans=min(dp[0][n-1][0],dp[0][n-1][1]);
printf("%d\n",ans);
}
然后还有方法二:
这个方法其实你是玩了游戏才知道怎么一回事,不然单纯想可能想不到这个结论性的结果,就是求最长回文子序列,因为大家想象一下,其实我们是通过变色使得左右两边同时变一色,好比消消乐时我们为了消灭更多不得不成堆消灭而不是见一个消灭一个,这样不仅浪费的步数而且肯定不是最终的 答案。
那么答案究竟是什么呢?
ans=len-(LCS+1)/2,为什么是这样呢???
大家需要观察一下,最坏的情况就是:1 2 3 4 5,需要翻4遍,如果运气好就是 1 2 1 4 5,少了一步。
就是根据这个进行推导得到这个式子,然后进行处理即可。
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10;
int dp[N][N];
int main()
{
int n,x,len;
scanf("%d",&n);
vector < int > a(1),b;
for(int i=0;i<n;i++){
scanf("%d",&x);
if( x!=a.back() )
a.push_back(x);
}
len=a.size()-1;
//printf("%d\n",len);
b=a;
reverse(b.begin()+1,b.end());
for(int i=1;i<=len;i++){
for(int j=1;j<=len;j++){
if(a[i]==b[j])
dp[i][j]=dp[i-1][j-1]+1;
else{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
}
int ans=len-(dp[len][len]+1)/2;
printf("%d\n",ans);
}