1. 程式人生 > >noip2013小朋友的數字(dp)

noip2013小朋友的數字(dp)

題目描述 Description
有n個小朋友排成一列。每個小朋友手上都有一個數字,這個數字可正可負。規定每個小朋友的特徵值等於排在他前面(包括他本人)的小朋友中連續若干個(最少有一個)小朋友手上的數字之和的最大值。
作為這些小朋友的老師,你需要給每個小朋友一個分數,分數是這樣規定的:第一個小朋友的分數是他的特徵值,其它小朋友的分數為排在他前面的所有小朋友中(不包括他本人),小朋友分數加上其特徵值的最大值。
請計算所有小朋友分數的最大值,輸出時保持最大值的符號,將其絕對值對p取模後輸出。

輸入描述 Input Description
第一行包含兩個正整數n、p,之間用一個空格隔開。
第二行包含n個數,每兩個整數之間用一個空格隔開,表示每個小朋友手上的數字。

輸出描述 Output Description
輸出只有一行,包含一個整數,表示最大分數對p取模的結果。

樣例輸入 Sample Input
[Sample 1]
5 997
1 2 3 4 5
[Sample 2]
5 7
-1 -1 -1 -1 -1

樣例輸出 Sample Output
[Sample 1]
21
[Sample 2]
-1
題解:
主要考察語文能力,關鍵把題讀懂。
讀完題就能發現這是個(幾乎是裸的)最大子段和問題。對於最大子段和問題,我們有O(N)的演算法。 具體的做法是這樣的:當前要求第I位及之前的最大子段和,如果第(I-1)位及之前的最大子段和大於0,則顯然這一位取了也未嘗不可(不會減少),也就是當前這一位和前面一段連線起來。否則的話,就新開一段——把前面的最大子段和改成0以後繼續往下掃描。 如果一定要說這是DP也可以。
這樣樸素的做能得50分,
在計算特徵值與分數的過程中記錄一下最大值可以的得到80分
原因在與最後兩個點的分數值超過了longlong。
進一步分析可以發現除了第一個小朋友外剩下的小朋友的分數值是不下降的。所以對於一個小朋友他的分數只有兩種情況。
1.如果他的前一個小朋友的特徵值大於0,那他的分數就為前一個小朋友的分數加上前一個小朋友的特徵值。更新當前最大值。
2.如果他的前一個小朋友的特徵值小於0,那他的分數就為第二個小朋友的分數。
當一個小朋友的分數值大於1000000000時我們取模
因為第一個小朋友的分數不會大於1000000000,所以我們就可以在計算過程中判斷出來是否有小朋友的分數大於第一個小朋友。
這樣就可以拿到滿分。。

#include<iostream>
#include<cstdio> 
using namespace std;
long long maxx,a[1000001],n,m,p,f[1000001]={0},b[1000001],ff[1000001],ans;
bool zf;
int main()
{
    cin>>n>>p;
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    f[1]=a[1];maxx=a[1];
    ff[1]=a[1];
    for (int i=2;i<=n;i++)
      {
        f[i]=max(f[i-1
]+a[i],a[i]); ff[i]=max(maxx,f[i]); maxx=ff[i]; } b[1]=ff[1]; b[2]=ff[1]+b[1]; if (b[2]>=b[1]) zf=true; maxx=b[2]; for (int i=3;i<=n;i++) { if (ff[i-1]>0) { b[i]=b[i-1]+ff[i-1]; if (b[i]>b[1]) zf=true; if (b[i]>1000000000) b[i]%=p; } else b[i]=b[2]; } if (!zf) ans=b[1]; else ans=b[n]; ans%=p; printf("%lld",ans); }