目錄
題目1 : 數組分拆
時間限制:10000ms
單點時限:1000ms
內存限制:256MB
描述
小Ho得到了一個數組作爲他的新年禮物,他非常喜歡這個數組!
在仔細研究了幾天之後,小Ho成功的將這個數組拆成了若干段,並且每段的和都不爲0!
現在小Ho希望知道,這樣的拆分方法一共有多少種?
兩種拆分方法被視作不同,當且僅當數組斷開的所有位置組成的集合不同。
輸入
每組輸入的第一行爲一個正整數N,表示這個數組的長度
第二行爲N個整數A1~AN,描述小Ho收到的這個數組
對於40%的數據,滿足1<=N<=10
對於100%的數據,滿足1<=N<=, |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]=dp[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]=a[j] (0<=j<=i),則若suma[i]!=suma[j],則a[j+1]+a[j+2]+....+a[i]!=0)
可是由於求和的存在,這是一個O()的dp,仍需優化思路.
這裏我們調整思路,定義一個dp數組的前綴和數組sumdp,sumdp[i]=dp[j] (0<=j<=i),則狀態轉移方程可修改爲
dp[i]=sumdp[i-1]-dp[j] (0<=j<i && suma[i]==suma[j])
我們發現其實我們可以用一個map m把所有已計算出的dp[j]以suma[j]爲鍵做一個統計,即m[x]=dp[j] (0<=j<isuma[j]==x)
則dp[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;
}