牛客练习赛67:D题 牛妹爱数列
3 / 10 3/10 3/10
不是很难
给你一个长度为 n n n 的01串。
你有两种操作:
操作1:某一位转换(1变0,0变1)
操作2:区间转换,区间 [ 1 , K ] [1,K] [1,K]中的全部数字都转换(1变0,0变1)
问你最少的操作次数,使得整个串全为0
1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1≤n≤105
10
1 0 1 1 0 0 0 1 0 0
3
样例解释:
第一次使用(1)操作, 把2改掉: 1 1 1 1 0 0 0 1 0 0
第二次使用(2)操作, 把1-4全部改掉: 0 0 0 0 0 0 0 1 0 0
第三次使用(1)操作, 把8改掉: 0 0 0 0 0 0 0 0 0 0
【思路一:DP】
( 出自队友禾硕 )
定义dp[ i ][ j ]为:
d p [ i ] [ 0 ] 表 示 前 i 位 都 翻 转 成 0 的 最 少 次 数 d p [ i ] [ 1 ] 表 示 前 i 位 都 翻 转 成 1 的 最 少 次 数 dp[\,i\,][\,0\,]表示前i位都翻转成0的最少次数\\ dp[\,i\,][\,1\,]表示前i位都翻转成1的最少次数\\ dp[i][0]表示前i位都翻转成0的最少次数dp[i][1]表示前i位都翻转成1的最少次数
状态转移也比较简单。
若 s [ i ] = 0 s[i]=0 s[i]=0那么:
d p [ i ] [ 0 ] = min { d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + 1 } d p [ i ] [ 1 ] = min { d p [ i − 1 ] [ 0 ] + 1 , d p [ i − 1 ] [ 1 ] + 1 } dp[i][0]=\min \Big \{dp[i-1][0],dp[i-1][1]+1\Big \}\\ dp[i][1]=\min \Big \{dp[i-1][0]+1,dp[i-1][1]+1\Big \}\\ dp[i][0]=min{dp[i−1][0],dp[i−1][1]+1}dp[i][1]=min{dp[i−1][0]+1,dp[i−1][1]+1}
当然, s [ i ] = 1 s[i]=1 s[i]=1时的dp状态 也可以简单地得到。思路同上。
【思路二:贪心】
我们从右向左枚举。
当前枚举到第 i i i 位。
若遇到的数字为0,不管它。
若遇到一个1,该数字与其左右两边都不同,则单独翻转它。
否则,即遇到一群的1(至少两个,我们设为 k k k 个),我们翻转区间 [ 1 , i ] [1,i] [1,i]。
至于这样为什么是正确的,我们稍微yy一下:
若 我 们 翻 转 区 间 [ 1 , i ] , 再 翻 转 区 间 [ 1 , i − k + 1 ] , 与 单 独 翻 转 两 个 1 是 等 价 的 若 我 们 翻 转 区 间 [ 1 , i ] , 左 边 可 能 有 一 些 更 麻 烦 的 区 间 被 我 们 解 决 了 。 \color{white}若我们翻转区间[1,i],再翻转区间[1,i-k+1],与单独翻转两个1是\color{blue}等价的\\ \color{white}若我们翻转区间[1,i],左边可能有一些更麻烦的区间被我们解决了。 若我们翻转区间[1,i],再翻转区间[1,i−k+1],与单独翻转两个1是等价的若我们翻转区间[1,i],左边可能有一些更麻烦的区间被我们解决了。
更麻烦的区间是指 1111101111 1111101111 1111101111。
若我们单独操作,需要翻转两次才能使之全0。
但是若我们翻转区间 [ 1 , i ] [1,i] [1,i] 的时候,存在一个子区间 [ 1 , m ] [1,m] [1,m]使得它为麻烦的区间
那么麻烦的区间会变成 0000010000 0000010000 0000010000,即只需简单翻转一次即可。
若我们遇到了不麻烦的子区间 [ 1 , m ] [1,m] [1,m],比如 00000100000 00000100000 00000100000:
我们在翻转 [ 1 , i ] [1,i] [1,i]时,该不麻烦的子区间变为了麻烦的子区间。
但是这样的操作并不会使得总次数增加。
【两个例子】
每个例子第一种为优先区间翻转操作,第二种为优先个别翻转操作。
(1)左边有麻烦的子区间
11111011000 → 00000100000 → 00000000000 ( 最 优 ) 11111011000 \rightarrow 00000100000\rightarrow 00000000000(最优) 11111011000→00000100000→00000000000(最优)
11111011000 → 11111010000 → 11111000000 → 00000000000 11111011000 \rightarrow 11111010000 \rightarrow 11111000000 \rightarrow 00000000000 11111011000→11111010000→11111000000→00000000000
(2)左边有不麻烦的子区间
00000100110 → 11111011000 → 00000100000 → 00000000000 ( 无 差 别 ) 00000100110 \rightarrow 11111011000\rightarrow 00000100000\rightarrow00000000000 (无差别) 00000100110→11111011000→00000100000→00000000000(无差别)
00000100110 → 00000100100 → 00000100000 → 00000000000 00000100110 \rightarrow 00000100100\rightarrow 00000100000\rightarrow00000000000 00000100110→00000100100→00000100000→00000000000
综上考虑,
k ≥ 2 时 , 翻 转 区 间 [ 1 , i ] 总 是 优 于 单 独 翻 转 两 个 数 字 的 。 \color{red}k\ge2时,翻转区间[1,i]总是优于单独翻转两个数字的。 k≥2时,翻转区间[1,i]总是优于单独翻转两个数字的。
稍微优化一下
我们每次都翻转一个区间,时间复杂度会很高。
我们设置一个翻转标记 b j bj bj,初始化为0,表示翻转的奇偶次数。
若 b j bj bj 与 s [ i ] s[i] s[i] 相同,那么等价于该位目前是0。
若进行了一次区间翻转,那么 b j ∧ = 1 bj \,\wedge=1 bj∧=1
时间复杂度: O ( N ) O(N) O(N)
/*
_ __ __ _ _
| | \ \ / / | | (_)
| |__ _ _ \ V /__ _ _ __ | | ___ _
| '_ \| | | | \ // _` | '_ \| | / _ \ |
| |_) | |_| | | | (_| | | | | |___| __/ |
|_.__/ \__, | \_/\__,_|_| |_\_____/\___|_|
__/ |
|___/
*/
int aa[MAX];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&aa[i]);
int bj = 0;
int ans = 0;
for(int i=n;i>=1;--i){
if(aa[i]==bj)continue;
int cnt = 0;
while(i>=1 && aa[i]!=bj)--i,cnt++;
i++;
if(cnt==1)ans+=cnt;
else {
bj ^= 1;
ans++;
}
}
printf("%d",ans);
return 0;
}