首页 > 试题广场 >

刷墙

[编程题]刷墙
  • 热度指数:3750 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
最近小明搬到了新家,他正在粉刷墙壁,但是不幸的是他粉刷的墙壁并不理想。他的墙壁是一个长度为 的格子,每个格子用0表示红色,用1表示蓝色。现在墙壁是一个非常混乱的颜色。他想将墙壁涂成左边全是蓝色右边全是红色,可以将墙壁刷成全是红色或者蓝色。请问他至少需要粉刷多少个格子墙壁刷成他想要的样子?

数据范围:
进阶:时间复杂度,空间复杂度

输入描述:
第一行一个整数
第二行个长度为的01串,0表示红色,1表示蓝色。


输出描述:
输出一个整数表示最少粉刷次数。
示例1

输入

3
001

输出

1

说明

只需要将最后一个刷成红色。 

预处理

我们的目的是让左边都为1,右边都为0。通过一次遍历,计算每个位置包括自己在内,左边有多少个0,右边有多少个1,两者之和减去1(减去1是因为当前位置的颜色不改)就是颜色边界为当前位置时的刷墙代价。再遍历一遍枚举颜色边界的可能性,计算出最小代价就行。
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        char[] wall = br.readLine().toCharArray();
        int[] left = new int[n];     // 包括自己,左边0的个数,这些是要被刷成1的
        int[] right = new int[n];    // 包括自己,右边1的个数,这些是要被刷成0的
        for(int i = 0; i < n; i++){
            if(i == 0){
                left[i] = wall[i] == '0'? 1: 0;
                right[n - i - 1] = wall[n - i - 1] == '1'? 1: 0;
            }else{
                left[i] = left[i - 1] + (wall[i] == '0'? 1: 0);
                right[n - i - 1] = right[n - i] + (wall[n - i - 1] == '1'? 1: 0);
            }
        }
        int cost = n;
        for(int i = 0; i < n; i++){
            cost = Math.min(cost, left[i] + right[i] - 1);
        }
        System.out.println(cost);
    }
}

复杂度分析

开辟了left和right数组,空间复杂度为O(n);遍历两遍数组,时间复杂度为O(n)。
发表于 2022-02-23 13:04:02 回复(0)
import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        in.nextLine();
        String s = in.nextLine();
        int len = s.length();
        int count = 0;
        int min = len;
        for(int i = 0; i < len; i++){ // 遍历统计1的个数
            if(s.charAt(i) == '1'){
                count++;
            }
        }
        min = Math.min(count,n-count); // 全刷成一种颜色,需要的最小次数
        int left1 = 0;
        for(int i = 0; i < len; i++){  // 再次遍历,把第i个元素当成分割点(包括i)
            if(s.charAt(i) == '1'){
                left1++;               // i左边的1个数(包括i),则左边0个数有:(i+1)-left1 【即左边总元素个数-1的个数】,这些0需要刷成1
            }                          // 则右边还有count-left1个1,这些1需要刷成0,
            int sum = i + 1 - left1 + count - left1;
            if(sum < min){ // 所以总共需要刷 (i+1)-left1 + count - left1
                min = sum; // 保存最小刷新次数
            }
        }
        System.out.println(min);
    }
}
发表于 2021-08-02 21:53:16 回复(3)

考虑第 个格子的左边线作为分界,左 1 右 0 ,左边假设全涂好了,右边

假如 第 i + 1 个房间是 1,那现在就不用涂了,反之要补上。

DP 的初值是一共几个 1

dp[i] 的意义是,选第 i 个时的花费。最后取 min 即可

input()
room = input()
dp = [0] * (1 + len(room))
dp[0] = room.count("1")
for i in range(1, len(room) + 1):
    dp[i] = dp[i - 1] + int(room[i - 1] == "0") - int(room[i - 1] == "1")
print(min(dp))
发表于 2021-05-01 22:26:45 回复(0)
咋个左边蓝右边红,又是全是红全是蓝,出题的是没学懂语文吧
发表于 2022-02-27 13:48:50 回复(3)
 JAVA  两个最简单的【动态规划 
    说它简单是因为它简单的都不像动态规划,只能说运用了动态规划思想,但你也可以从其他思想角度得到同样的方程:

    翻译题目:蓝色是1,红色是0 => 首先题目要求序列左边都是连续的1右边都是连续的0,特殊情况下可以全为1或全为0,则:

dp[i]表示当前0~i个中有几个1 
        = 前0~i-1中1的个数为dp[i-1],在加上第i个中1的个数
        = dp[i-1] + array[i];

min[i] 为左边i个墙块全刷蓝色,右边剩余墙块全刷红色,时所需要的刷墙次数
        = 现在需要的刷墙次数
        = 左边i个墙块中需要刷蓝的个数 + 右边length - i个墙块中需要刷红的个数
        = 左边i个元素中0的个数 + 右边length - i个元素中1的个数
        = (i - dp[i-1]) + (dp[length - 1] - dp[i-1])

再令min[i] = 各min[i]取最小 
        = MIN{min[i-1], 现在需要的刷墙次数} 
        = MIN{min[i-1], (i - dp[i-1]) + (dp[length - 1] - dp[i-1])}

即可输出最终的各刷墙方法中的最小值:min[length]
【从动态规划的集合角度来看:min[i]为左边墙面为0~i的各刷墙集合中的最少刷墙次数】

import java.util.*;
import java.io.*;

public class Main{
    int paint(int length, int[] array){
        int[] dp = new int[length], min = new int[length+1];
        dp[0] = array[0];
        for(int i = 1 ; i<length ; i++)
            dp[i] = dp[i-1] + array[i];
        min[0] = dp[length - 1];            //左边留0个墙面刷蓝,右边剩余所有墙面刷红(即1的个数)
        for(int i = 1 ; i<=length ; i++)
            min[i] = Math.min(min[i-1], (i - dp[i-1]) + (dp[length-1] - dp[i-1]));
        return min[length];
    }

        // 单纯处理输入数据,不用看
    public static void main(String[] args) throws IOException{
        Main main = new Main();
        BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
        int i = 0, len = Integer.parseInt(buf.readLine());
        int[] ar = new int[len];
        for(char c : buf.readLine().toCharArray()){
            ar[i] = c - '0';
            i++;
        }
        System.out.println(main.paint(len, ar));
    }
}


编辑于 2022-05-19 15:09:21 回复(0)
import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        if (n == 1) {
            System.out.println(0);
            return;
        } 
        String s = sc.next();
        char[] chars = s.toCharArray();
        int[] cntZero = new int[n];
        int[] cntOne = new int[n];
        cntOne[0] = chars[0] == '1' ? 1 : 0;
        for(int i = 1; i < n; i++) {
            cntOne[i] = cntOne[i - 1];
            if(chars[i]=='1') cntOne[i]++; 
        }
        cntZero[n - 1] = chars[n - 1] == '0' ? 1 : 0;
        for(int i = n - 2; i >= 0; i--) {
            cntZero[i] = cntZero[i + 1];
            if(chars[i]=='0') cntZero[i]++; 
        }
        int sameColor = Math.min(n - cntZero[0], n - cntOne[n - 1]);
        int diffColor = n;
        for (int i = 0; i < n - 1; i++) {
            int times = i + 1 - cntOne[i] + (n - i - 1) - cntZero[i+1];
            diffColor = Math.min(diffColor, times);
        }
        System.out.println(Math.min(sameColor, diffColor));
        
    }    
}
发表于 2022-02-08 13:52:22 回复(0)
string str;
int num;
cin>>num>>str
int numRightAll_0 = 0;
int numLeftAll_1 = 0;
for(int i=0; i<num; i++){
    if(str[i] != '0') numRightAll_0++;
}
int minNum = numRightAll_0;
for(int i=0; i<num; i++){
    if (str[i] != '0'){
        numRightAll_0--;
    }
    else{
        numLeftAll_1++;
    }
    minNum = min(minNum, numRightAll_0 + numLeftAll_1);  
}
cout<<minNum<<endl;

发表于 2021-07-30 10:30:27 回复(0)
var n = 3;
var list = [0, 0, 1];
var least_time = function (n, list) {
    var left_length = (list.length + 1) / 2;
    var right_length = list.length - left_length;
    var num0 = 0;
    var num1 = 0;
    var list_left = list.slice(0, left_length - 1);
    var list_right = list.slice(left_length, list.length - 1);
    for (var i = 0; i < left_length; i++) {
        if (list_left[i] == 0) {
            num0 = num0 + 1
        } else {
            num1 = num1 + 1
        }
    }
    var numleft = 0
    if (num0 >= num1) {
        numleft = num1;
    } else {
        numleft = num0;
    }
    num0 = 0;
    num1 = 0
    for (var i = 0; i < right_length; i++) {
        if (list_right[i] == 0) {
            num0 = num0 + 1
        } else {
            num1 = num1 + 1
        }
    }
    var numright = 0
    if (num0 >= num1) {
        numright = num1;
    } else {
        numright = num0;
    }
    var total = numleft + numright;
    return total;
}
var time_n = least_time(n, list);
console.log(time_n)
发表于 2021-04-09 15:24:38 回复(0)
C++题解
动态规划法?
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
int main(){
    int n;
    cin>>n;
    string s;
    cin>>s;
    int array[n];
    int sum=0;
    for(int i=0;i<n;i++){
        array[i]=s[i]-'0';
        sum=sum+array[i];
    }
    int num=0;
    int nl=0,nr=sum;
    int ARRAY[n+1];
    
    if(sum==0)
        cout<<num;
    else{
            ARRAY[0]=nl+nr;
            for(int j=0;j<n;j++){
                if (array[j]==0){
                    nl=nl+1;
                }
                else{
                    nr=nr-1;
                }
                ARRAY[j+1]=nl+nr;
            }
            num=ARRAY[0];
            for(int a=1;a<n+1;a++){
                if(ARRAY[a]<num)
                    num=ARRAY[a];
            }
            cout<<num;
    }

}


发表于 2022-07-14 14:18:27 回复(0)
前缀和
import math

class Solution:
    def solution(self, n: int, arr: str):
        pre_one = [0] * n
        pre_zero = [0] * n
        total_one = total_zero = 0
        for i in range(n):
            if arr[i] =='1':
                total_one += 1
            pre_one[i] = total_one
            if arr[i] == '0':
                total_zero += 1
            pre_zero[i] = total_zero
        ans = math.inf
        for i in range(n):
            one = pre_one[i]
            zero = pre_zero[i]
            # left one right zero  # left zero right one
            ans = min(ans, zero +  total_one - one)
        ans = min(ans, total_one, total_zero)
        return ans
if __name__=='__main__':
    n = int(input())
    wall = input()
    solution = Solution()
    res = solution.solution(n, wall)
    print(res)


发表于 2022-07-13 15:14:53 回复(0)
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

int main(){
    int n;
    cin >> n;
    string s;
    cin >> s;
    vector<int> nums(n,0);
    vector<int> sums(n,0);
    int sum = 0;
    for (int i = 0; i < n; i++) {
        nums[i] = s[i] - '0';
    }
    for (int i = 0; i < n; i++) {
         sum = sum + nums[i];
         sums[i] = sum;
    }
    int res_rb = min(sums[n-1],n-sums[n-1]);
    int flag = 0;
    int res = n;
    for(int i=0;i<n;i++){
        flag += nums[i];
        res = min(res,i+1-sums[i] + sums[n-1] - sums[i]);
    }
    res = min(res,res_rb);
    cout << res << endl;
    return 0;
}
发表于 2022-07-06 17:36:25 回复(0)
思路:利用栈的思想,把要分析的字符串先转换为字符数组,倒叙让字符依次入栈;
当为‘1’时,直接入栈;
当为‘0’时,判断栈是否为空,如果不为空,让栈顶元素出栈(‘0’字符永不入栈)。
为什么这么想?
当我们依次倒叙的时候,
遇到0直接去掉:1101000与1101的结果是一致的,不需要变化。
遇到1时判断:
如果前一位是0,则count++,然后把‘01’去掉,如:110101的结果 = 1101的结果 + 1;
否则如果前一位是1,则变成‘11’形式,在往前推,会变为(m个1,n个0)01(y个1)形式;
仔细分析可发现:(m个1,n个0)01(y个1)的 结果等于(m个1,n个0)(y个1)的结果+1;【完美符合栈先进后出的规律!】
        
import java.util.Scanner;
import java.util.Stack;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        in.nextLine();
        String s = in.nextLine();
        System.out.println(shuaQiang(n,s));
    }
    public static int shuaQiang(int n, String s) {
        int count = 0;
        char[] c = s.toCharArray();
        Stack<Character> stack = new Stack();
        for (;n>0;n--) {
            if (c[n-1] == '1') {
                stack.push(c[n-1]);
            }
            else {
                if (!stack.empty()) {
                    stack.pop();
                    count++;
                }
            }
        }
        return count;
    }
}
发表于 2022-06-06 16:52:47 回复(0)
n=int(input())
s=input()
count=0
left=0
for i in range(n):
    if s[i]=='1':
    count+=1
    min1=min(count,n-count)
for i in range(n):
    if s[i]=='1':
    left+=1
    min2=i+1-left+count-left
#i+1-left:左边红球个数;count-left:右边蓝球个数
    if min1>min2:
    min1=min2
print(min1)
发表于 2022-05-14 20:26:56 回复(0)
他想将墙壁涂成左边全是蓝色右边全是红色,可以将墙壁刷成全是红色或者蓝色。这句话什么意思啊 这墙壁到底什么样子的?

发表于 2022-05-01 23:49:56 回复(2)
以某个位置i为分界线,[0,i]范围全部置为1,(i, n)范围全部置为0。枚举i,计算以i为分界线,需要涂改多少个格子。
为了方便计算,利用一个数组cnts存储前(i+1)个格子中(i从下标0开始),'1'的个数。
以下标i为分界线,[0, i]全部置为'1',那么需要把[0, i]范围的 '0' 格子涂改,需要涂改格子的个数为i+1-cnts[i+1];然后把(i, n)范围格子全部置为'0', 那么需要把(i, n)范围的'1'格子涂改,需要涂改的格子数为cnts[n]-cnts[i+1];枚举i,取所有操作次数中的最小值。
#include<vector>
#include<iostream>
#include<math.h>
using namespace std;
int test01(int n, string& s) {
    // 计算前i个格子中,蓝色格子的数量
    vector<int> cnts(n + 1, 0);
    int c = 0;
    for (int i = 0; i < n; ++i) {
        if (s[i] == '1') {
            c++;
        }
        cnts[i + 1] = c;
    }
    // 初始时,将所有格子刷为红色
    int res = cnts[n];
    // 计算前i+1个格子,全部刷为蓝色的操作数
    for (int i = 0; i < n; ++i) {
        int t = i + 1 - cnts[i + 1] + cnts[n] - cnts[i + 1];
        res = min(res, t);
    }
    return res;
}
int main() {
    int n;
    string s;
    cin >> n;
    cin >> s;
    cout << test01(n, s) << endl;;
    return 0;
}


编辑于 2022-04-07 22:24:14 回复(0)
public static void main(String[] args) {  Scanner scanner = new Scanner(System.in);  int s1 = Integer.valueOf(scanner.nextLine());  String s2 = scanner.nextLine();  if (s2.startsWith("1") || s2.endsWith("0")) {  System.out.println(0);  return;  }  char[] ss = s2.toCharArray();  int start = -1, end = -1;  for (int i = 0, j = ss.length - 1; i != j -1 || i < j; i++, j--) {  if (ss[i] == '1') {  start = i;  break;  }  if (ss[j] == '0') {  end = j;  break;  }  }   if (start == -1) {  System.out.println(s1 - end - 1);  } else if (end == -1) {  System.out.println(start);  } else {  System.out.println(s1/2);  } }
发表于 2022-03-25 18:22:08 回复(0)

简单双指针

import java.util.*; public class Main {         public static void main(String[] args) {             Scanner sc = new Scanner(System.in);             int n = Integer.parseInt(sc.nextLine());             char[] cs = sc.nextLine().toCharArray();             sc.close();             int left0 = 0, left1 = 0;             int right0 = 0, right1 = 0;             for (int i = n - 1; i >= 0; i--) {                 if (cs[i] == '1') {                     right1++;                 } else {                     right0++;                 }             }             int min = right1;             for (int i = 0; i < n; i++) {                 if (cs[i] == '1') {                     right1--;                     left1++;                 } else {                     left0++;                     right0--;                 }                 min = Math.min(min, left0 + right1);             }             System.out.println(min);         }     }

发表于 2022-03-23 23:43:44 回复(0)
let n = Number(readline())
let rawStr = readline()
let min = n
let left0=0,right1=0
for(let i = 0;i<n;++i){
    if(rawStr[i]=='1')++right1
}
for(let i = 0;i<=n;++i){
    if(i>0){
        if(rawStr[i-1]=='0')left0++
        else right1--
    }
    if(left0+right1<min)min = left0+right1
}
print(min)
加一个隔板,从左到右遍历隔板的位置。首先隔板在最左边,进行初始化。隔板每向右移动一次,就更新left0的个数和right1的个数。复杂度是O(n)
发表于 2021-12-09 15:59:45 回复(0)
import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.nextLine();
        String s = sc.nextLine();
        int[] dp = new int[n + 1];
        int count = 0;
        for (char c : s.toCharArray()) {
            if (c == '1') {
                count++;
            }
        }
        dp[0] = count;

        for (int i = 1; i < n + 1; i++) {
            if (s.charAt(i - 1) == '0') {
                dp[i] = dp[i - 1] + 1;
            } else {
                dp[i] = dp[i - 1] - 1;
            }
        }
        System.out.println(Arrays.stream(dp).min().getAsInt());
    }
}


发表于 2021-11-26 11:15:13 回复(0)
var n=parseInt(readline())
    var str=readline()
    function rush(n,str){
    str=str.split('')
    var arr0 = []
    var arr1 = []
    var count=0
    var min =0
    for(var i=0;i<n;i++){
        if(str[i]=='1'){
            arr1.push(str[i])
            count++
        }
    }
    min=Math.min(count,n-count)
    var left1=0
    for(var i =0;i<n ;i++){
        if(str[i]=='1'){
            left1++
        }
        var sum = i + 1-left1+count-left1
        if(sum<min){
            min=sum
        }
    }
    console.log(min);
}
    rush(n,str)
参考评论写了JS的代码,谢谢大佬
发表于 2021-08-17 20:09:21 回复(0)