数组分拆

 

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

小Ho得到了一个数组作为他的新年礼物,他非常喜欢这个数组!

在仔细研究了几天之后,小Ho成功的将这个数组拆成了若干段,并且每段的和都不为0!

现在小Ho希望知道,这样的拆分方法一共有多少种?

两种拆分方法被视作不同,当且仅当数组断开的所有位置组成的集合不同。

输入

每组输入的第一行为一个正整数N,表示这个数组的长度

第二行为N个整数A1~AN,描述小Ho收到的这个数组

对于40%的数据,满足1<=N<=10

对于100%的数据,满足1<=N<=105, |Ai|<=100

输出

对于每组输入,输出一行Ans,表示拆分方案的数量除以(109+7)的余数。

样例输入

5
1 -1 0 2 -2

样例输出

5

dalao 思路:http://hihocoder.com/discuss/question/5645

开始很容易直接想到一个O(n^2) 的动态规划。 令dp[i]表示i个数不同种划分数,sum[j,i]表示数组下标从j到i的和。 从划分思想,比较容易想到:

dp[i]=∑dp[j] (j<i&&sum[j,i]!=0)

其中sum[j,i]可以由前缀数组O(1)的得到。

但明显这题N<1e5,复杂度不够。 继续优化。 对于dp[i]求和这一步我们同样用前缀数组优化。 定义: dp_sum[i] = ∑ dp[i]

那么对于dp[i] = dp_sum[i-1] - 数量(∑ sum[j+1,i]=0)

对于sum[j+1,i]=0,我们两边同时加上sum[0,j],就变成:sum[0,i] = sum[0,j]。问题变成减去出现sum[0,i] = sum[0,j]的数量,也就是减去前缀和为sum[0,i]的数量。 ``于是我们只需要一个Hash表去保存前缀和sum[0,i]出现的个数。时间复杂度O(1)求出sum[0,i]的数量。

则转移方程:dp[i] = dp_sum[i-1] - Hash[sum[0,i]]

o(n^2) 复杂度 超时

package Daily;

import java.util.Scanner;

public class Main_233 {
    int mod = 1000000007;
    public  void excute(){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int []nums = new int[n];
        for(int i = 0; i < n; i++){
            nums[i] = sc.nextInt();
        }
        sc.close();
        int  count = find(nums);
        System.out.println(count);
    }

    public  int find(int []nums){
        int dp[] = new int[nums.length + 1];
        dp[nums.length] = 1;
        for(int i = nums.length- 1; i >= 0; i --){
            int subSum = 0;
            int kind = 0;
            for(int k = i; k < nums.length ; i --){
                subSum += nums[k];
                if(subSum != 0 ){
                 kind += dp[k +1];
                }
            }
            dp[i] = kind;
        }
        return dp[0];
    }
    public static  void  main(String[] args){
        Main_233 m = new Main_233();
        m.excute();
    }
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const int mod= 1e9+7;
ll dp[N],sum[N],sum_dp[N];

map<ll,ll> Hash;

int main(){
     int n;
     while(~scanf("%d",&n)){
        memset(dp,0,sizeof(dp));
        sum[0] = 0;
        for(int i=1;i<=n;i++){
          int x;scanf("%d",&x);
          sum[i] = sum[i-1] + x;
        }

        dp[0] = 1;
        sum_dp[0] = 1;
        Hash[0] = 1;
        for(int i=1;i<=n;i++){
           dp[i] = (sum_dp[i-1] - Hash[sum[i]] + mod)%mod;
           sum_dp[i] = (sum_dp[i-1] + dp[i] + mod)%mod;
           Hash[sum[i]] = (Hash[sum[i]] +dp[i] + mod)%mod;
           //cout<<dp[i]<<" ";
        }
        //cout<<endl;
        printf("%lld\n",dp[n]);
     }
}

猜你喜欢

转载自blog.csdn.net/weixin_38970751/article/details/85247227
今日推荐