link
dp/贪心 2200
题意
给定
n
n
n 个元素,元素
i
i
i 具有值
a
i
a_i
ai 和颜色
c
i
c_i
ci,最初,对于所有
i
i
i,
c
i
=
0
c_i=0
ci=0。可以不限次数地进行以下操作:
选取三个元素
i
,
j
,
k
(
1
≤
i
<
j
<
k
≤
n
)
i,j,k(1\leq i<j<k\leq n)
i,j,k(1≤i<j<k≤n),其中
c
i
=
c
j
=
c
k
=
0
c_i=c_j=c_k=0
ci=cj=ck=0且
a
i
=
a
k
a_i=a_k
ai=ak,令
c
j
c_j
cj=1。
问
m
a
x
Σ
c
i
max \Sigma c_i
maxΣci为多少?
思路
- 贪心
显然对于每个数字,我们只需要考虑第一个和最后一个出现的位置,用pos[i]
表示数字 i i i 最后一次出现的位置。如果数字之间不影响,每次可以将第一个出现的位置和最后一个出现的位置之间的 c c c 均改为1。关键在于有重合部分的情况。对每个 i i i,用cover
表示当前区间的右端点,r
表示从1到 i i i 中 a i a_i ai 的最右端点,若cover > i
,则cnt++,表示这个数可以变为1。否则更新cover=r
void solve() {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
pos[a[i]] = i;
}
int r = 0, cnt = 0, cover = 0;
for(int i = 1; i <= n; i++) {
r = max(r, pos[a[i]]);
if(cover > i) cnt++;
else cover = r;
}
cout << cnt << endl;
}
- dp
dp[i]
表示前 i i i 个数中0的数量。每个数肯定都可以取0,故dp[i]=min(dp[i-1]+1, dp[i])
,cur
表示右端点最大值,dp[cur]=min(dp[cur], dp[i] + 1)
(因为cur
对应的的左端点一定小于等于i
所以可以把i+1
到cur-1
之间均变为1,也就是说可以做到只有cur
处为0,故dp[cur]可以取dp[i]+1
)
void solve() {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
pos[a[i]] = i;
}
memset(dp,0x3f,sizeof(dp));
dp[0] = 0;
int cur = pos[a[1]];
for(int i = 1; i <= n; i++) {
dp[i] = min(dp[i-1] + 1, dp[i]);
cur = max(cur, pos[a[i]]);
dp[cur] = min(dp[i] + 1, dp[cur]);
}
cout << n - dp[n] << endl;
}