hiho233 数组分拆

版权声明:转载我的原创博客情注明出处 https://blog.csdn.net/qq_31964727/article/details/85332550

目录

题目1 : 数组分拆

题意分析:

1.题是什么?

2.思路(参考自官方思路)

3.小收获

ac代码:


题目1 : 数组分拆

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

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

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

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

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

输入

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

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

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

对于100%的数据,满足1<=N<=10^{5}, |Ai|<=100

输出

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

样例输入

5
1 -1 0 2 -2

样例输出

5

题意分析:

1.题是什么?

    给你一个十万大小的数组,要你将之拆分为不同段,问每个段和都不为0的不同拆法总数(结果求余1e9+7)).

2.思路(参考自官方思路)

    首先一看到统计满足条件的情况总数自然该想到dp,问题是dp数组如何设计以及dp转移方程.

    我们这里设原数组为数组a,dp数组中dp[i]的意义是将a[0]~a[i-1]拆分的方法总数,则可以获得dp数组的转移方程如下:

        dp[i]=\sumdp[j] (0<=j<i && a[j+1]+a[j+2]+....+a[i]!=0)

    通过预处理a数组前缀和,关于a[j+1]+a[j+2]+....+a[i]!=0的判断可以以如下方式O(1)搞定:

    (定义一个a数组前缀和数组suma,suma[i]=\suma[j] (0<=j<=i),则若suma[i]!=suma[j],则a[j+1]+a[j+2]+....+a[i]!=0)

    可是由于求和的存在,这是一个O(n^{2})的dp,仍需优化思路.

    这里我们调整思路,定义一个dp数组的前缀和数组sumdp,sumdp[i]=\sumdp[j] (0<=j<=i),则状态转移方程可修改为

         dp[i]=sumdp[i-1]-\sumdp[j] (0<=j<i && suma[i]==suma[j])

    我们发现其实我们可以用一个map m把所有已计算出的dp[j]以suma[j]为键做一个统计,即m[x]=\sumdp[j] (0<=j<isuma[j]==x)

    则\sumdp[j] (0<=j<i && suma[i]==suma[j])的计算也可以以O(1)完成,故而如下优化为一个O(n)的dp.

        dp[i]=sumdp[i-1]-m[suma[i]]

    实际编写中关于dp,sumdp,suma由于只会用到上一个数据,故而我没用数组,只用的一个int,最大化节约空间.

3.小收获

    map中如果直接使用下标方式如m[1000]尝试访问键为1000的值,若map中存在则正常返回存的值,可是若不存在则视为一种插入操作,返回0的同时执行m.insert(make_pair(1000,0)),因此最好先检查键是否存在,否则以下标方式访问可能污染map.实验代码如下

#include <iostream>
#include <map>
using namespace std;

void solve(){
	map<int,int> m;
	m.insert(make_pair(0,1));
	cout<<"下标访问前的map:"<<endl; 
	for(map<int,int>::iterator ite=m.begin();ite!=m.end();ite++){
		cout<<ite->first<<" "<<ite->second<<endl;
	} 
	
	cout<<endl<<"键为1000的访问返回: "<<m[1000]<<endl;//下标访问 
	
	cout<<"下标访问后的map:"<<endl; 
	for(map<int,int>::iterator ite=m.begin();ite!=m.end();ite++){
		cout<<ite->first<<" "<<ite->second<<endl;
	} 
}

int main(){
	solve();
	return 0;
}

ac代码:

#include <iostream>
#include <vector>
#include <map>
using namespace std;

const int maxn=1e5+5; 
const int mod=1e9+7;
vector<int> a(maxn,0);

void solve(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];  
	
	map<int,int> m;
	m.insert(make_pair(0,1));
	int dp=1,suma=0,sumdp=1;
	for(int i=0;i<n;i++){
		suma+=a[i];
		dp=sumdp;
		if(m.count(suma)){
			dp=(dp-m[suma]+mod)%mod;//避免负数 
			m[suma]=(m[suma]+dp)%mod;
		}
		else m[suma]=dp; 
		sumdp=(sumdp+dp)%mod;
	}
	cout<<dp<<endl;
}

int main(){
	solve();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_31964727/article/details/85332550
233