http://codeforces.com/contest/269/problem/B
题目意思
n颗植物,种类有m种。种在一个长度无限的实数轴上。
现在可以通过改变一些植物的坐标,使得最后所有植物的种类沿x轴正方向为不下降序列。
求最少要改变多少植物的位置。
解法一:
这是我现场的解法,复杂度O(n*m)
dp[i][j],表示到第j颗植物,使其以种类i结尾,最少需要改变的数量。
dp[1][j],特殊算出。
dp[i][0] = 0
dp[i][j] = min(dp[i-1][k] + sum[j][i] - sum[k][i]) | 0<=k<=j) = min(dp[i-1][k] - sum[k][i]) | 0<=k<=j) + sum[j][i]
sum[j][k]表示编号1..k中有多少种类为j的植物
看起来复杂度像是m*n*n,不过,其实min(dp[i-1][k] - sum[k][i])这个,一边DP,一边算就可以了,因为k的范围是越来越大嘛。
这题植物的坐标是没什么用的,因为一定是严格递增的,而实际上,任意两个实数之间可以种下无数棵。所有需要改变的植物,拔走后,最后再考虑就可以了。
比赛时的代码比较SB,还写单调队列来优化空间。。实际上没比滚动数组优化。。而实际上,不优化内存也完全够。
1 #include<cmath> 2 #include<cstdio> 3 #include<string> 4 #include<cstdlib> 5 #include<cstring> 6 #include<iostream> 7 #include<algorithm> 8 #include<queue> 9 using namespace std; 10 int a[5010],dp[5010],sum[5010]; 11 int l,r,d[5010]; 12 int n,m; 13 int main() 14 { 15 while (~scanf("%d%d",&n,&m)) 16 { 17 for(int i=1;i<=n;i++)scanf("%d%*lf",a+i); 18 dp[0]=0; 19 for(int i=1;i<=n;i++) 20 dp[i]=dp[i-1] + (a[i]!=1); 21 for(int i=2;i<=m;i++){ 22 for(int j=1;j<=n;j++) 23 sum[j]=sum[j-1]+(a[j]!=i); 24 l=0;r=-1; 25 for(int j=n;j>=0;j--){ 26 while(l<=r && dp[j]-sum[j]<=dp[d[r]]-sum[d[r]])r--; 27 d[++r]=j; 28 } 29 for(int j=n;j>0;j--){ 30 while(d[l]>j)l++; 31 dp[j]=dp[d[l]]-sum[d[l]]+sum[j]; 32 } 33 } 34 printf("%d\n",dp[n]); 35 } 36 return 0; 37 }
解法二:贪心
植物的种类构成一个序列
ans = n - len(最长不下降子序列)
最长不下降子序列有nlogn解法,网上资料也很多,就不再重复。