《算法竞赛·快冲300题》每日一题:“乘积”

算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。


乘积” ,链接: http://oj.ecustacm.cn/problem.php?id=1781

题目描述

【题目描述】 给你一个长度为n的序列,序列中的元素只包括1和-1。
请问有多少个连续的子序列乘积为正数。
【输入格式】 输入第一行为正整数n。(n不超过10^6)
第二行包含n个整数。
【输出格式】 输出一个整数表示答案。
【输入样例】

4
1 1 -1 -1

【输出样例】

6

题解

  这是一道经典题。偶数个-1相乘是正数,奇数个-1相乘是负数。本题显然可以用DP来做,下面设计DP状态和转移方程。
  设输入的序列是a[1]~a[n],定义DP状态为:
    dp[i][0]:以a[i]结尾的积为1的个数
    dp[i][1]:以a[i]结尾的积为-1的个数
  例如样例的{1, 1, -1, -1},有:dp[1][0]=1,dp[2][0]=2,dp[3][0]=0,dp[4][0]=3。
  状态转移方程:
  (1)若a[i]=1
    dp[i][0] = dp[i-1][0] + 1,积为1的个数再加1(就是加自己)
    dp[i][1] = dp[i-1][1],积继续为-1,只能1乘以积为-1的dp
  (2)若a[i]=-1
    dp[i][0] = dp[i-1][1],-1乘以积为-1的dp,积才为1
    dp[i][1] = dp[i-1][0] + 1, -1乘以积为1的dp,积才为-1,再加上1(自己)
  最后,把所有dp[i][0]相加,就是答案。DP的总计算复杂度为O(n)。
  本题也有其他解法,请自己思考。
【重点】 线性DP。

C++代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int a[N];
long long dp[N][2];  //dp[i][0]:以a[i]结尾的积为1的个数;dp[i][1]:以a[i]结尾的积为-1的个数
long long ans;
int main(){
    
    
    int n; cin >> n;
    for(int i = 1;i <= n;i ++) cin >> a[i];
    for(int i = 1;i <= n;i ++){
    
    
        if(a[i] == 1){
    
    
            dp[i][0] = dp[i-1][0] + 1;  //积为1的个数再加1(就是加自己)
            dp[i][1] = dp[i-1][1];      //积继续为-1,只能1乘以积为-1的dp
        }
        else if(a[i] == -1){
    
    
            dp[i][0] = dp[i-1][1];      // -1乘以积为-1的dp ,积才为1
            dp[i][1] = dp[i-1][0] + 1;  // -1 乘以积为1的dp,积才为-1, 再加上1(自己)
        }
    }
    for(int i=1;i<=n;i++)  ans = ans + dp[i][0];   //将所有积为1的dp求和,即为总个数
    cout << ans << endl;
    return 0;
}

Java代码

import java.util.Scanner;
public class Main {
    
    
    static final int N = 1000010;
    static int[] a = new int[N];
static long[][] dp = new long[N][2]; 
//dp[i][0]:以a[i]结尾的积为1的个数;dp[i][1]:以a[i]结尾的积为-1的个数
    static long ans;
    public static void main(String[] args) {
    
    
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        for (int i = 1; i <= n; i++)  a[i] = sc.nextInt();        
        for (int i = 1; i <= n; i++) {
    
    
            if (a[i] == 1) {
    
    
                dp[i][0] = dp[i - 1][0] + 1;  //积为1的个数再加1(就是加自己)
                dp[i][1] = dp[i - 1][1];      //积继续为-1,只能1乘以积为-1的dp
            } else if (a[i] == -1) {
    
    
                dp[i][0] = dp[i - 1][1];      // -1乘以积为-1的dp ,积才为1
                dp[i][1] = dp[i - 1][0] + 1;  // -1 乘以积为1的dp,积才为-1, 再加上1(自己)
            }
        }
        for (int i=1; i<=n; i++)   ans += dp[i][0]; //将所有积为1的dp求和,即为总个数
        System.out.println(ans);
    }
}

Python代码

N = 1000010
a = [0] * N
dp = [[0] * 2 for _ in range(N)]  
# dp[i][0]:以a[i]结尾的积为1的个数;dp[i][1]:以a[i]结尾的积为-1的个数
ans = 0
n = int(input())
a[1:n + 1] = list(map(int, input().split()))
for i in range(1, n + 1):
    if a[i] == 1:
        dp[i][0] = dp[i - 1][0] + 1        # 积为1的个数再加1(就是加自己)
        dp[i][1] = dp[i - 1][1]            # 积继续为-1,只能1乘以积为-1的dp
    elif a[i] == -1:
        dp[i][0] = dp[i - 1][1]            # -1乘以积为-1的dp ,积才为1
        dp[i][1] = dp[i - 1][0] + 1        # -1 乘以积为1的dp,积才为-1, 再加上1(自己)

for i in range(1, n+1): ans += dp[i][0]    # 将所有积为1的dp求和,即为总个数
print(ans)

猜你喜欢

转载自blog.csdn.net/weixin_43914593/article/details/131810636