zoukankan      html  css  js  c++  java
  • CF1481E Sorting Books

    E. Sorting Books

    这个官方题解只讲了转移方程,根本没讲原理,这里全部重新做个解释。

    我们先考虑答案的范围。如果我们贪心,那么最多操作次数为 \(n\) 次,且一本书最多会被移动一次。

    定状态

    我们令 \(l_i,r_i\) 分别为第 \(i\) 种颜色从左往右数最早出现/最晚出现的位置。令 \(f_i\) 为在最后将所有书归好类后,\([i,n]\) 内最多有多少本书无需移动。明显,我们需要倒序枚举。最后的答案为 \(n-f_1\)

    我们记 \(tot_{i,j}\) 为在 \([j,n]\) 中,颜色为 \(i\) 的书本有多少本。

    列状态转移方程

    \[f_i=\max\begin{cases} f_{i+1}&1\leq i<n\\ tot_{a_i,i}&i\neq l_{a_i}\\ tot_{a_i,i}+f_{r_{a_i}+1}&i=l_{a_i} \end{cases}\]

    • 首先我们考虑的是 \(i\) 这本书要移动,那么此时最多可以保留不动的就是 \(f_{i+1}\) 本。
    • 如果 \(i\) 这本书不移动,那么显然颜色为 \(a_i\) 的书都不要动,即 \(tot_{a_i,i}\) 本。那为什么在 \(i\neq l_{a_i}\),不加上 \(f_{r_{a_i}+1}\) 呢?我们可以重点关注一下样例:
    5
    1 2 2 1 3
    

    若加上了,我们得到的 \(f\) 数组为 \(4,4,3,2,1\)(下标从 \(1\) 开始)。明显是错误的,应该为 \(3\)。为什么会错呢?我们来具体分析发生了甚么。

    从本质出发,为什么可以保持颜色为 \(a_i\) 的书不移动,就是因为把 \([i,r_{a_i}]\) 中颜色不为 \(a_i\) 的全部移走。如果过早地进行了与有区间的合并,可能就会有两个区间有部分重叠。只有等所有颜色为 \(a_i\) 的数都出现过了,才可以进行区间的合并。

    那上面那个样例说事,我们来理清关系:

    • \(f_1=f_2\)
    • \(f_2=f_4+1\)
    • \(f_4=f_5+1\)

    好,到这里,就出现了问题。我们已经将 \([4,4],[5,5]\) 合并为了 \([4,5]\)。而在 \([2,3]\) 出现后,我们又进行了合并,有区间 \([2,5]\)。说明我们不可能将 \(2\) 移走了。我饿么您此时为了做出最优决策,只能将第一个 \(1\) 移走。明显是错误的。因为在 \([1,4]\) 区间可以被计算时,\([4,4]\) 已经“名花有主”了。

    边界&初值

    无。好干脆啊。

    //Don't act like a loser.
    //This code is written by huayucaiji
    //You can only use the code for studying or finding mistakes
    //Or,you'll be punished by Sakyamuni!!!
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    
    int read() {
    	char ch=getchar();
    	int f=1,x=0;
    	while(ch<'0'||ch>'9') {
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') {
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	return f*x;
    }
    
    const int MAXN=5e5+10; 
    
    int n;
    int a[MAXN],l[MAXN],r[MAXN],f[MAXN],tot[MAXN];
    
    signed main() {
    	cin>>n;
    	for(int i=1;i<=n;i++) {
    		a[i]=read();
    		if(!l[a[i]]) {
    			l[a[i]]=i;
    		}
    	}
    	for(int i=n;i;i--) {
    		if(!r[a[i]]) {
    			r[a[i]]=i;
    		}
    	}
    	
    	for(int i=n;i;i--) {
    		tot[a[i]]++;
    		if(i==l[a[i]]) {
    			f[i]=max(tot[a[i]]+f[r[a[i]]+1],f[i+1]);
    		}
    		else {
    			f[i]=max(f[i+1],tot[a[i]]);//正确
    		//	f[i]=max(tot[a[i]]+f[r[a[i]]+1],f[i+1];//错误
    		}
    	}
    	
    	cout<<n-f[1]<<endl;
    	return 0;
    }
    
  • 相关阅读:
    涉猎
    linq to sql中的自动缓存(对象跟踪)
    Java的起源和发展
    Java为什么需要保留基本数据类型
    JDK各版本新增的主要特性
    【转】整理:著名软件是使用什么语言写的?
    Struts2框架学习
    json格式
    sqlserver开窗函数改造样例
    说一下这次的求职经历。
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/CF1481E.html
Copyright © 2011-2022 走看看